Repository: chef/automate
Pull request #8067 (2023-07-18 05:22:32)

fixed known hosts getting corrupt and added message for verify preprovision error
### :nut_and_bolt: Description: What code changed, and why?

<!-- /!\ Please ensure that you are NOT disclosing any customer information without their consent /!\ -->
PR contains below changes:

- Earlier when we were running the verify command before provision it used to give error `Error reading "/hab/a2_deploy_workspace/terraform/output.auto.tfvars": open /hab/a2_deploy_workspace/terraform/output.auto.tfvars: no such file or directory`. This does not mean anything to our users. We have added a message saying: 
`Cannot run verify command before provision in AWS deployment. Please run 'chef-automate provision-infra config.toml --airgap-bundle automate.aib' to provision your infra`

- Chef-automate sshutil created entries in the known host file adding the fingerprint manually and was using it for further ssh verification. This use to cause the ssh knownhost file to get corrupted. We are now checking if :

  - Last line of known hosts contains some entry, then add next entry in new line
  - Last line of known hosts is empty, then add entry in same line

### :chains: Related Resources

### :+1: Definition of Done

### :athletic_shoe: How to Build and Test the Change

### :white_check_mark: Checklist

**All PRs** must tick these:

- [ ] I have read the [CONTRIBUTING document](https://github.com/chef/automate/blob/master/CONTRIBUTING.md).
- [ ] 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:

- [ ] Is the code clear? *(complicated code or lots of comments--subdivide and use well-named methods, meaningful variable names, etc.)*
- [ ] Consistency checked? *(user notifications, user prompts, visual patterns, code patterns, variable names)*
- [ ] Repeated code blocks eliminated? *(adapt and reuse existing components, blocks, functions, etc.)*
- [ ] Spelling, grammar, typos checked? *(at a minimum use `make spell` in any component directory)*
- [ ] 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="1792" alt="Screenshot 2023-07-18 at 11 20 51 AM" src="https://github.com/chef/automate/assets/59958706/425f6594-f86d-4b6a-8d6c-db1eb0f2ffd2">

<img width="1792" alt="Screenshot 2023-07-19 at 2 12 50 PM" src="https://github.com/chef/automate/assets/59958706/3b462c36-f4b6-4482-b3c7-d2ba0ecb65d0">



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


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

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

[![Bug](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/bug-16px.png 'Bug')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=BUG) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=BUG)  
[![Vulnerability](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/vulnerability-16px.png 'Vulnerability')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=VULNERABILITY) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/security_hotspot-16px.png 'Security Hotspot')](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067)  
[![Code Smell](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/code_smell-16px.png 'Code Smell')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=CODE_SMELL) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=CODE_SMELL)

[![0.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.0%')](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_coverage&view=list) [0.0% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_coverage&view=list)  
[![0.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/3-16px.png '0.0%')](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_duplicated_lines_density&view=list)


sonarqube-for-infrastructure-prod[bot]: **SonarQube Quality Gate**

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

[![Bug](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/bug-16px.png 'Bug')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=BUG) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=BUG)  
[![Vulnerability](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/vulnerability-16px.png 'Vulnerability')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=VULNERABILITY) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/security_hotspot-16px.png 'Security Hotspot')](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067)  
[![Code Smell](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/common/code_smell-16px.png 'Code Smell')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=CODE_SMELL) [![A](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/RatingBadge/A-16px.png 'A')](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&resolved=false&types=CODE_SMELL)

[![10.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '10.0%')](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_coverage&view=list) [10.0% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_coverage&view=list)  
[![0.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/3-16px.png '0.0%')](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=8067&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/VERSION b/VERSION
index 4598dc565..807a48905 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.7.186
\ No newline at end of file
+4.7.202
\ No newline at end of file
diff --git a/components/automate-backend-deployment/README.md b/components/automate-backend-deployment/README.md
index bfa0490fc..57e00b228 100644
--- a/components/automate-backend-deployment/README.md
+++ b/components/automate-backend-deployment/README.md
@@ -1,5 +1,6 @@
 # Automate::Backend::Deployment
 
+
 This provides the `automate-backend-deployment` package.
 
 This package will build a package using terraform/a2ha-terraform, inspecs, test, certs and Makefile.
diff --git a/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go b/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go
index a736482b4..6c6d98016 100644
--- a/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go
+++ b/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment.go
@@ -5,6 +5,7 @@ import (
 	"crypto/x509"
 	"encoding/pem"
 	"fmt"
+	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
@@ -12,6 +13,8 @@ import (
 	"github.com/chef/automate/components/automate-cli/pkg/status"
 	"github.com/chef/automate/components/automate-deployment/pkg/toml"
 	"github.com/chef/automate/components/local-user-service/password"
+	"github.com/chef/automate/lib/httputils"
+	"github.com/chef/automate/lib/logger"
 	"github.com/chef/automate/lib/platform/command"
 	"github.com/chef/automate/lib/stringutils"
 	ptoml "github.com/pelletier/go-toml"
@@ -19,19 +22,24 @@ import (
 )
 
 const (
-	DEPLOY    = "deploy"
-	PROVISION = "provision"
+	DEPLOY       = "deploy"
+	PROVISION    = "provision"
+	TOKEN_URL    = "http://169.254.169.254/latest/api/token"
+	METADATA_URL = "http://169.254.169.254/latest/meta-data/iam/info"
 )
 
 type awsDeployment struct {
 	config     AwsConfigToml
 	configPath string
 	AWSConfigIp
+	httpRequestClient httputils.HTTPClient
 }
 
 func newAwsDeployemnt(configPath string) *awsDeployment {
+	log := logger.NewLogrusStandardLogger()
 	return &awsDeployment{
-		configPath: configPath,
+		configPath:        configPath,
+		httpRequestClient: httputils.NewClient(log),
 	}
 }
 
@@ -205,7 +213,10 @@ func (a *awsDeployment) validateConfigFields() *list.List {
 func (a *awsDeployment) validateEnvFields() *list.List {
 	errorList := list.New()
 	if len(a.config.Aws.Config.Profile) < 1 {
-		errorList.PushBack("Invalid or empty aws profile name")
+		err := a.isIamRolePresent()
+		if err != nil {
+			errorList.PushBack("Invalid local AWS Profile name or Bastion IAM role, Please Check your local AWS Profile name or Bastion IAM Role is configured properly")
+		}
 	}
 	if len(a.config.Aws.Config.Region) < 1 {
 		errorList.PushBack("Invalid or empty aws region")
@@ -444,3 +455,21 @@ func (a *awsDeployment) getAwsHAIp() error {
 	a.AWSConfigIp = *ConfigIp
 	return nil
 }
+
+func (a *awsDeployment) isIamRolePresent() error {
+	_, tokenResponseBody, err := a.httpRequestClient.MakeRequestWithHeaders(http.MethodPut, TOKEN_URL, nil, "X-aws-ec2-metadata-token-ttl-seconds", "21600")
+	if err != nil {
+		return fmt.Errorf("error while getting the token value: %v", err)
+	}
+
+	token := string(tokenResponseBody)
+
+	resp, _, err := a.httpRequestClient.MakeRequestWithHeaders(http.MethodGet, METADATA_URL, nil, "X-aws-ec2-metadata-token", token)
+	if err != nil {
+		return fmt.Errorf("error while getting the response for IAM role: %v", err)
+	}
+	if resp.StatusCode != 200 {
+		return errors.New("Please check if Bastion has attached an IAM Role to it")
+	}
+	return nil
+}
diff --git a/components/automate-cli/cmd/chef-automate/certRotate.go b/components/automate-cli/cmd/chef-automate/certRotate.go
index c9a940fd4..5f4a696b4 100644
--- a/components/automate-cli/cmd/chef-automate/certRotate.go
+++ b/components/automate-cli/cmd/chef-automate/certRotate.go
@@ -14,8 +14,11 @@ import (
 	"github.com/chef/automate/api/config/deployment"
 	"github.com/chef/automate/components/automate-cli/pkg/docs"
 	"github.com/chef/automate/components/automate-cli/pkg/status"
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
 	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/logger"
 	"github.com/chef/automate/lib/platform/command"
+	"github.com/chef/automate/lib/sshutils"
 	"github.com/chef/automate/lib/stringutils"
 	"github.com/chef/toml"
 	"github.com/pkg/errors"
@@ -132,7 +135,19 @@ type certRotateFlags struct {
 }
 
 type certRotateFlow struct {
-	FileUtils fileutils.FileUtils
+	fileUtils   fileutils.FileUtils
+	sshUtil     sshutils.SSHUtil
+	writer      *cli.Writer
+	pullConfigs PullConfigs
+}
+
+func NewCertRotateFlow(fileUtils fileutils.FileUtils, sshUtil sshutils.SSHUtil, writer *cli.Writer, pullConfigs PullConfigs) *certRotateFlow {
+	return &certRotateFlow{
+		fileUtils:   fileUtils,
+		sshUtil:     sshUtil,
+		writer:      writer,
+		pullConfigs: pullConfigs,
+	}
 }
 
 type patchFnParameters struct {
@@ -141,6 +156,7 @@ type patchFnParameters struct {
 	fileName      string
 	timestamp     string
 	remoteService string
+	concurrent    bool
 	infra         *AutomateHAInfraDetails
 	flagsObj      *certRotateFlags
 	skipIpsList   []string
@@ -182,7 +198,11 @@ func init() {
 
 func certRotateCmdFunc(flagsObj *certRotateFlags) func(cmd *cobra.Command, args []string) error {
 	return func(cmd *cobra.Command, args []string) error {
-		c := certRotateFlow{FileUtils: &fileutils.FileSystemUtils{}}
+		log, err := logger.NewLogger("text", "info")
+		if err != nil {
+			return err
+		}
+		c := NewCertRotateFlow(&fileutils.FileSystemUtils{}, sshutils.NewSSHUtilWithCommandExecutor(sshutils.NewSshClient(), log, command.NewExecExecutor()), writer, NewPullConfigs(&AutomateHAInfraDetails{}, &SSHUtilImpl{}))
 		return c.certRotate(cmd, args, flagsObj)
 	}
 }
@@ -214,7 +234,7 @@ func (c *certRotateFlow) certRotate(cmd *cobra.Command, args []string, flagsObj
 		currentCertsInfo, err := certShowFlow.fetchCurrentCerts()
 
 		if err != nil {
-			return errors.New("Error occured while fetching current certs.")
+			return errors.Wrap(err, "Error occured while fetching current certs")
 		}
 
 		if flagsObj.timeout < DEFAULT_TIMEOUT {
@@ -266,13 +286,14 @@ func (c *certRotateFlow) certRotateFrontend(sshUtil SSHUtil, certs *certificates
 
 	// Creating and patching the required configurations.
 	config := fmt.Sprintf(FRONTEND_CONFIG, certs.publicCert, certs.privateCert, certs.publicCert, certs.privateCert)
-
+	concurrent := true
 	patchFnParam := &patchFnParameters{
 		sshUtil:       sshUtil,
 		config:        config,
 		fileName:      fileName,
 		timestamp:     timestamp,
 		remoteService: remoteService,
+		concurrent:    concurrent,
 		infra:         infra,
 		flagsObj:      flagsObj,
 		skipIpsList:   skipIpsList,
@@ -305,33 +326,35 @@ func (c *certRotateFlow) certRotatePG(sshUtil SSHUtil, certs *certificates, infr
 
 	skipIpsList := c.compareCurrentCertsWithNewCerts(remoteService, certs, flagsObj, currentCertsInfo)
 	c.skipMessagePrinter(remoteService, SKIP_IPS_MSG_CERT_ROTATE, flagsObj.node, skipIpsList)
-
+	concurrent := false
 	patchFnParam := &patchFnParameters{
 		sshUtil:       sshUtil,
 		config:        config,
 		fileName:      fileName,
 		timestamp:     timestamp,
 		remoteService: remoteService,
+		concurrent:    concurrent,
 		infra:         infra,
 		flagsObj:      flagsObj,
 		skipIpsList:   skipIpsList,
 	}
 
-	//patching on PG
+	// patching on PG
 	err := c.patchConfig(patchFnParam)
 	if err != nil {
 		return err
 	}
-
 	// ignore patching of root-ca when node flag is provided
 	if flagsObj.node != "" {
 		return nil
 	}
-	//get frontend ips to skip root-ca patch
-	skipIpsList = c.getFrontEndIpsForSkippingRootCAPatching(remoteService, certs.rootCA, infra, currentCertsInfo)
+	skipIpsList, err = c.getSkipIpsListForPgRootCAPatching(infra, sshUtil, certs)
+	if err != nil {
+		return err
+	}
 	c.skipMessagePrinter("frontend", SKIP_FRONT_END_IPS_MSG_PG, "", skipIpsList)
 
-	// Patching root-ca to frontend-nodes for maintaining the connection.
+	//Patching root-ca to frontend-nodes for maintaining the connection.
 	filenameFe := "pg_fe.toml"
 	remoteService = "frontend"
 	// Creating and patching the required configurations.
@@ -341,6 +364,7 @@ func (c *certRotateFlow) certRotatePG(sshUtil SSHUtil, certs *certificates, infr
 	patchFnParam.fileName = filenameFe
 	patchFnParam.remoteService = remoteService
 	patchFnParam.skipIpsList = skipIpsList
+	patchFnParam.concurrent = true
 
 	err = c.patchConfig(patchFnParam)
 	if err != nil {
@@ -349,6 +373,30 @@ func (c *certRotateFlow) certRotatePG(sshUtil SSHUtil, certs *certificates, infr
 	return nil
 }
 
+func (c *certRotateFlow) getSkipIpsListForPgRootCAPatching(infra *AutomateHAInfraDetails, sshUtil SSHUtil, certs *certificates) ([]string, error) {
+
+	c.pullConfigs.setInfraAndSSHUtil(infra, sshUtil)
+	//fetching current config from automate
+	automateCurrentConfig, err := c.pullConfigs.pullAutomateConfigs()
+	if err != nil {
+		return nil, err
+	}
+
+	//get frontend ips to skip root-ca patch
+	skipIpsList := c.getFrontendIPsToSkipRootCAPatchingForPg(automateCurrentConfig, certs.rootCA, infra)
+
+	//fetching current config from chefServer
+	chefServerCurrentConfig, err := c.pullConfigs.pullChefServerConfigs()
+
+	if err != nil {
+		return nil, err
+	}
+	newskipIpsList := c.getFrontendIPsToSkipRootCAPatchingForPg(chefServerCurrentConfig, certs.rootCA, infra)
+	skipIpsList = append(skipIpsList, newskipIpsList...)
+
+	return skipIpsList, nil
+}
+
 // certRotateOS will rotate the certificates of OpenSearch.
 func (c *certRotateFlow) certRotateOS(sshUtil SSHUtil, certs *certificates, infra *AutomateHAInfraDetails, flagsObj *certRotateFlags, currentCertsInfo *certShowCertificates) error {
 	if isManagedServicesOn() {
@@ -392,13 +440,14 @@ func (c *certRotateFlow) certRotateOS(sshUtil SSHUtil, certs *certificates, infr
 	} else {
 		config = fmt.Sprintf(OPENSEARCH_CONFIG, certs.rootCA, certs.adminCert, certs.adminKey, certs.publicCert, certs.privateCert, fmt.Sprintf("%v", adminDn), fmt.Sprintf("%v", nodesDn))
 	}
-
+	concurrent := false
 	patchFnParam := &patchFnParameters{
 		sshUtil:       sshUtil,
 		config:        config,
 		fileName:      fileName,
 		timestamp:     timestamp,
 		remoteService: remoteService,
+		concurrent:    concurrent,
 		infra:         infra,
 		flagsObj:      flagsObj,
 		skipIpsList:   skipIpsList,
@@ -421,31 +470,23 @@ func (c *certRotateFlow) certRotateOS(sshUtil SSHUtil, certs *certificates, infr
 		}
 
 	}
-
-	//fetching server_name from automate
-	plgcng := NewPullConfigs(infra, sshUtil)
-	automatesConfig, err := plgcng.pullAutomateConfigs()
-
-	if err != nil {
-		return err
-	}
-
-	oldCn := getOldCn(automatesConfig)
-
 	// Patching root-ca to frontend-nodes for maintaining the connection.
-	cn := nodesCn
 	filenameFe := "os_fe.toml"
 	remoteService = "frontend"
 
-	skipIpsList = c.getFrontEndIpsForSkippingCnAndRootCaPatching(certs.rootCA, cn, oldCn, flagsObj.node, currentCertsInfo, infra)
+	skipIpsList, err = c.getSkipIpsListForOsRootCACNPatching(infra, sshUtil, certs, nodesCn, flagsObj)
+	if err != nil {
+		return err
+	}
 	skipMessage := ""
+
 	// Creating and patching the required configurations.
 	var configFe string
 	if flagsObj.node != "" {
-		configFe = fmt.Sprintf(OPENSEARCH_FRONTEND_CONFIG_IGNORE_ROOT_CERT, cn)
+		configFe = fmt.Sprintf(OPENSEARCH_FRONTEND_CONFIG_IGNORE_ROOT_CERT, nodesCn)
 		skipMessage = SKIP_FRONT_END_IPS_MSG_CN
 	} else {
-		configFe = fmt.Sprintf(OPENSEARCH_FRONTEND_CONFIG, certs.rootCA, cn)
+		configFe = fmt.Sprintf(OPENSEARCH_FRONTEND_CONFIG, certs.rootCA, nodesCn)
 		skipMessage = SKIP_FRONT_END_IPS_MSG_OS
 	}
 	c.skipMessagePrinter(remoteService, skipMessage, "", skipIpsList)
@@ -454,6 +495,7 @@ func (c *certRotateFlow) certRotateOS(sshUtil SSHUtil, certs *certificates, infr
 	patchFnParam.fileName = filenameFe
 	patchFnParam.remoteService = remoteService
 	patchFnParam.skipIpsList = skipIpsList
+	patchFnParam.concurrent = true
 
 	err = c.patchConfig(patchFnParam)
 	if err != nil {
@@ -462,6 +504,32 @@ func (c *certRotateFlow) certRotateOS(sshUtil SSHUtil, certs *certificates, infr
 	return nil
 }
 
+func (c *certRotateFlow) getSkipIpsListForOsRootCACNPatching(infra *AutomateHAInfraDetails, sshUtil SSHUtil, certs *certificates, nodesCn string, flagsObj *certRotateFlags) ([]string, error) {
+
+	c.pullConfigs.setInfraAndSSHUtil(infra, sshUtil)
+	//fetching current config from automate
+	automateCurrentConfig, err := c.pullConfigs.pullAutomateConfigs()
+	if err != nil {
+		return nil, err
+	}
+
+	// Patching root-ca to frontend-nodes for maintaining the connection.
+	cn := nodesCn
+
+	skipIpsList := c.getFrontIpsToSkipRootCAandCNPatchingForOs(automateCurrentConfig, certs.rootCA, cn, flagsObj.node, infra)
+
+	//fetching current config from chefServer
+	chefServerCurrentConfig, err := c.pullConfigs.pullChefServerConfigs()
+
+	if err != nil {
+		return nil, err
+	}
+	newskipIpsList := c.getFrontIpsToSkipRootCAandCNPatchingForOs(chefServerCurrentConfig, certs.rootCA, cn, flagsObj.node, infra)
+	skipIpsList = append(skipIpsList, newskipIpsList...)
+
+	return skipIpsList, nil
+}
+
 func patchOSNodeDN(flagsObj *certRotateFlags, patchFnParam *patchFnParameters, c *certRotateFlow, nodesDn string) error {
 
 	peerConfig := fmt.Sprintf(OPENSEARCH_DN_CONFIG_FOR_PEERS, fmt.Sprintf("%v", nodesDn))
@@ -473,7 +541,7 @@ func patchOSNodeDN(flagsObj *certRotateFlags, patchFnParam *patchFnParameters, c
 	patchFnParam.config = peerConfig
 	patchFnParam.timestamp = time.Now().Format("20060102150405")
 	patchFnParam.skipIpsList = []string{flagsObj.node}
-
+	patchFnParam.concurrent = false
 	err := c.patchConfig(patchFnParam)
 	if err != nil {
 		return err
@@ -483,22 +551,60 @@ func patchOSNodeDN(flagsObj *certRotateFlags, patchFnParam *patchFnParameters, c
 	return nil
 }
 
-func getOldCn(automatesConfig map[string]*deployment.AutomateConfig) string {
+func (c *certRotateFlow) getFrontIpsToSkipRootCAandCNPatchingForOs(automatesConfig map[string]*deployment.AutomateConfig, newRootCA string, newCn string, node string, infra *AutomateHAInfraDetails) []string {
+	oldRootCA := ""
 	oldCn := ""
-	for _, config := range automatesConfig {
+	skipIpsList := []string{}
+	for ip, config := range automatesConfig {
 		if config.Global.V1.External.Opensearch != nil && config.Global.V1.External.Opensearch.Ssl != nil {
 			if config.Global.V1.External.Opensearch.Ssl.ServerName != nil {
 				oldCn = config.Global.V1.External.Opensearch.Ssl.ServerName.Value
-				break
+				if c.getFrontEndIpsForSkippingCnAndRootCaPatching(newRootCA, newCn, oldCn, oldRootCA, node) {
+					if !isStringPresent(skipIpsList, ip) {
+						// If not present, append the string to the array
+						skipIpsList = append(skipIpsList, ip)
+					}
+				}
+			}
+			if config.Global.V1.External.Opensearch.Ssl.RootCert != nil {
+				oldRootCA = config.Global.V1.External.Opensearch.Ssl.RootCert.Value
+				if c.getFrontEndIpsForSkippingCnAndRootCaPatching(newRootCA, newCn, oldCn, oldRootCA, node) {
+					if !isStringPresent(skipIpsList, ip) {
+						// If not present, append the string to the array
+						skipIpsList = append(skipIpsList, ip)
+					}
+				}
+			}
+		}
+	}
+	return skipIpsList
+}
+
+func isStringPresent(arr []string, str string) bool {
+	for _, s := range arr {
+		if s == str {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *certRotateFlow) getFrontendIPsToSkipRootCAPatchingForPg(automatesConfig map[string]*deployment.AutomateConfig, newRootCA string, infra *AutomateHAInfraDetails) []string {
+	oldRootCA := ""
+	skipIpsList := []string{}
+	for ip, config := range automatesConfig {
+		if config.Global.V1.External.Postgresql.Ssl.RootCert != nil {
+			oldRootCA = config.Global.V1.External.Postgresql.Ssl.RootCert.Value
+			if oldRootCA == newRootCA {
+				skipIpsList = append(skipIpsList, ip)
 			}
 		}
 	}
-	return oldCn
+	return skipIpsList
 }
 
 // patchConfig will patch the configurations to required nodes.
 func (c *certRotateFlow) patchConfig(param *patchFnParameters) error {
-
 	f, err := os.Create(param.fileName)
 	if err != nil {
 		return err
@@ -508,7 +614,6 @@ func (c *certRotateFlow) patchConfig(param *patchFnParameters) error {
 		return err
 	}
 	f.Close()
-
 	var ips []string
 	if param.flagsObj.node != "" && param.remoteService != "frontend" {
 		isValid := c.validateEachIp(param.remoteService, param.infra, param.flagsObj)
@@ -525,24 +630,46 @@ func (c *certRotateFlow) patchConfig(param *patchFnParameters) error {
 
 	//collect ips on which need to perform action/cert-rotation
 	filteredIps := c.getFilteredIps(ips, param.skipIpsList)
+	filteredIpsString := strings.Join(filteredIps, ", ")
+	sshConfig := c.getSshDetails(param.infra)
+	sshConfig.hostIP = filteredIpsString
+	sshConfig.timeout = param.flagsObj.timeout
+	configRes := sshutils.SSHConfig{
+		SshUser:    sshConfig.sshUser,
+		SshPort:    sshConfig.sshPort,
+		SshKeyFile: sshConfig.sshKeyFile,
+		HostIP:     sshConfig.hostIP,
+		Timeout:    sshConfig.timeout,
+	}
 
-	// Defining set of commands which run on particular remoteservice nodes
 	var scriptCommands string
+	command := getScriptCommands(param, scriptCommands)
+	if !param.concurrent {
+		err = c.copyAndExecute(filteredIps, param.sshUtil, param.timestamp, param.remoteService, param.fileName, scriptCommands, param.flagsObj)
+		if err != nil {
+			return err
+		}
+	} else {
+		err = c.copyAndExecuteConcurrentlyToFrontEndNodes(filteredIps, configRes, param.timestamp, param.remoteService, param.fileName, command, param.flagsObj)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Defining set of commands which run on particular remoteservice nodes
+func getScriptCommands(param *patchFnParameters, scriptCommands string) string {
 	if param.remoteService == AUTOMATE || param.remoteService == CHEF_SERVER || param.remoteService == "frontend" {
 		scriptCommands = fmt.Sprintf(FRONTEND_COMMAND, PATCH, param.remoteService+param.timestamp, DATE_FORMAT)
 	} else if param.remoteService == POSTGRESQL || param.remoteService == OPENSEARCH {
 		scriptCommands = fmt.Sprintf(COPY_USER_CONFIG, param.remoteService+param.timestamp, param.remoteService)
 	}
-	err = c.copyAndExecute(filteredIps, param.sshUtil, param.timestamp, param.remoteService, param.fileName, scriptCommands, param.flagsObj)
-	if err != nil {
-		return err
-	}
-	return nil
+	return scriptCommands
 }
 
-// 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 {
-
 	var err error
 	var tomlFilePath string
 	for i := 0; i < len(ips); i++ {
@@ -574,6 +701,53 @@ func (c *certRotateFlow) copyAndExecute(ips []string, sshUtil SSHUtil, timestamp
 	return nil
 }
 
+// copyAndExecuteConcurrently will copy the toml file to each required nodes concurrently and then execute the set of commands.
+func (c *certRotateFlow) copyAndExecuteConcurrentlyToFrontEndNodes(ips []string, sshConfig sshutils.SSHConfig, timestamp string, remoteService string, fileName string, scriptCommands string, flagsObj *certRotateFlags) error {
+
+	sshConfig.Timeout = flagsObj.timeout
+	copyResults := c.sshUtil.CopyFileToRemoteConcurrently(sshConfig, fileName, remoteService+timestamp, false, ips)
+	isError := false
+	for _, result := range copyResults {
+		if result.Error != nil {
+			c.writer.Errorf("Remote copying automate-verify CLI failed on node : %s with error: %v\n", result.HostIP, result.Error)
+			isError = true
+		}
+	}
+	if isError {
+		return fmt.Errorf("remote copying failed on node")
+	}
+
+	fmt.Printf("\nStarted Applying the Configurations in %s node: %s \n", remoteService, ips)
+	excuteResults := c.sshUtil.ExecuteConcurrently(sshConfig, scriptCommands, ips)
+	for _, result := range excuteResults {
+		printCertRotateOutput(result, remoteService, writer)
+	}
+	return nil
+}
+
+func printCertRotateOutput(cmdResult sshutils.Result, remoteService string, writer *cli.Writer) {
+	writer.Printf("\n=======================================================================\n")
+
+	if cmdResult.Error != nil || strings.Contains(cmdResult.Output, "DeploymentServiceCallError") {
+		printCertRotateErrorOutput(cmdResult, remoteService, writer)
+	} else {
+		writer.Printf("Output for Host IP %s : \n%s", cmdResult.HostIP, cmdResult.Output+"\n")
+		writer.Success("Command is executed on node : " + cmdResult.HostIP + "\n")
+	}
+	writer.BufferWriter().Flush()
+}
+
+func printCertRotateErrorOutput(cmdResult sshutils.Result, remoteService string, writer *cli.Writer) {
+	isOutputError := false
+	if strings.Contains(cmdResult.Output, "DeploymentServiceCallError") {
+		isOutputError = true
+		writer.Failf(CMD_FAILED_MSG, remoteService, cmdResult.HostIP, cmdResult.Output)
+	}
+	if !isOutputError {
+		writer.Failf(CMD_FAILED_MSG, remoteService, cmdResult.HostIP, cmdResult.Error.Error())
+	}
+}
+
 // validateEachIp validate given ip with the remoteService cluster.
 func (c *certRotateFlow) validateEachIp(remoteService string, infra *AutomateHAInfraDetails, flagsObj *certRotateFlags) bool {
 	ips := c.getIps(remoteService, infra)
@@ -694,30 +868,11 @@ func (c *certRotateFlow) comparePublicCertAndPrivateCert(newCerts *certificates,
 	return skipIpsList
 }
 
-// getFrontEndIpsForSkippingRootCAPatching compare new root-ca and current root-ca of remoteService and returns ips to skip root-ca patching.
-func (c *certRotateFlow) getFrontEndIpsForSkippingRootCAPatching(remoteService string, newRootCA string, infra *AutomateHAInfraDetails, currentCertsInfo *certShowCertificates) []string {
-	skipIpsList := []string{}
-
-	if remoteService == POSTGRESQL {
-		if strings.TrimSpace(currentCertsInfo.PostgresqlRootCert) == newRootCA {
-			skipIpsList = append(skipIpsList, c.getIps("frontend", infra)...)
-		}
-	}
-
-	if remoteService == AUTOMATE {
-		if strings.TrimSpace(currentCertsInfo.AutomateRootCert) == newRootCA {
-			skipIpsList = append(skipIpsList, c.getIps(CHEF_SERVER, infra)...)
-		}
-	}
-
-	return skipIpsList
-}
-
 // getFrontEndIpsForSkippingCnAndRootCaPatching compare new root-ca and new cn with current root-ca and cn and returns ips to skip root-ca patching.
-func (c *certRotateFlow) getFrontEndIpsForSkippingCnAndRootCaPatching(newRootCA, newCn, oldCn, node string, currentCertsInfo *certShowCertificates, infra *AutomateHAInfraDetails) []string {
+func (c *certRotateFlow) getFrontEndIpsForSkippingCnAndRootCaPatching(newRootCA, newCn, oldCn, oldRootCA, node string) bool {
 	isRootCaSame := false
 
-	if strings.TrimSpace(currentCertsInfo.OpensearchRootCert) == newRootCA {
+	if strings.TrimSpace(oldRootCA) == newRootCA {
 		isRootCaSame = true
 	}
 
@@ -728,14 +883,14 @@ func (c *certRotateFlow) getFrontEndIpsForSkippingCnAndRootCaPatching(newRootCA,
 	}
 
 	if node == "" && isRootCaSame && isCnSame {
-		return c.getIps("frontend", infra)
+		return true
 	}
 
 	if node != "" && isCnSame {
-		return c.getIps("frontend", infra)
+		return true
 	}
 
-	return []string{}
+	return false
 }
 
 // skipMessagePrinter print the skip message
@@ -885,7 +1040,7 @@ func (c *certRotateFlow) getCertFromFile(certPath string, infra *AutomateHAInfra
 		}
 		return []byte(out), nil
 	}
-	return c.FileUtils.ReadFile(certPath)
+	return c.fileUtils.ReadFile(certPath)
 }
 
 // GetRemoteFileDetails returns the remote file details from the remotePath.
@@ -951,7 +1106,6 @@ func (c *certRotateFlow) getMerger(fileName string, timestamp string, remoteType
 	if err != nil {
 		return "", err
 	}
-
 	var (
 		dest interface{}
 		err1 error
diff --git a/components/automate-cli/cmd/chef-automate/gather-logs.go b/components/automate-cli/cmd/chef-automate/gather-logs.go
index e5949c69c..540edaeba 100644
--- a/components/automate-cli/cmd/chef-automate/gather-logs.go
+++ b/components/automate-cli/cmd/chef-automate/gather-logs.go
@@ -446,6 +446,8 @@ func runGatherLogsLocalCmd(outfileOverride string, logLines uint64) error {
 	g.AddCopiesFromPath("config", "/hab/svc")
 	g.AddCopiesFromPath("logs", "/hab/svc")
 
+	g.AddCopiesFromPath("pg_log", "/hab/svc")
+
 	// local status
 	g.AddCommand("df_h", "df", "-h")
 	g.AddCommand("df_i", "df", "-i")
diff --git a/components/automate-cli/cmd/chef-automate/ha_node_operations_utils.go b/components/automate-cli/cmd/chef-automate/ha_node_operations_utils.go
index b16182c07..ec130d7c4 100644
--- a/components/automate-cli/cmd/chef-automate/ha_node_operations_utils.go
+++ b/components/automate-cli/cmd/chef-automate/ha_node_operations_utils.go
@@ -23,7 +23,12 @@ import (
 )
 
 const (
-	TAINT_TERRAFORM        = "for x in $(terraform state list -state=/hab/a2_deploy_workspace/terraform/terraform.tfstate | grep module); do terraform taint $x; done"
+	TAINT_TERRAFORM = `for x in $(terraform state list -state=/hab/a2_deploy_workspace/terraform/terraform.tfstate | grep module); do terraform taint $x; done`
+
+	TAINT_EFS = `
+    terraform init;
+	for x in $(terraform state list -state=/hab/a2_deploy_workspace/terraform/destroy/aws/terraform.tfstate | grep "module.efs\[0\].null_resource.mount_efs");do terraform taint $x; done`
+
 	REMOVE_TERRAFORM_STATE = `S3_STATE=$(terraform state list -state=/hab/a2_deploy_workspace/terraform/destroy/aws/terraform.tfstate | grep ".aws_s3_bucket.createS3bucket");
 if [ "$S3_STATE" == "module.s3[0].aws_s3_bucket.createS3bucket" ]; then
 terraform state rm $S3_STATE
@@ -166,6 +171,11 @@ func (nu *NodeUtilsImpl) taintTerraform(path string) error {
 		if err := executeShellCommand("/bin/bash", []string{"-c", REMOVE_TERRAFORM_STATE}, filepath.Join(path, "destroy", "aws")); err != nil {
 			return err
 		}
+
+		if err := executeShellCommand("/bin/bash", []string{"-c", TAINT_EFS}, filepath.Join(path, "destroy", "aws")); err != nil {
+			return err
+		}
+
 	}
 	return executeShellCommand("/bin/sh", []string{"-c", TAINT_TERRAFORM}, path)
 }
diff --git a/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go b/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go
index 21a75e243..e816f7fa0 100644
--- a/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go
+++ b/components/automate-cli/cmd/chef-automate/initConfigHaTmpl.go
@@ -154,7 +154,7 @@ enable_custom_certs = false
 # ============== AWS network Config ============================
 ## === INPUT NEEDED ===
 
-# Eg.: profile = "default"
+# Eg.: profile = "default", Leave this field empty if you have an IAM role attached to the bastion machine.
 profile = ""
 
 # Eg.: region = "us-east-1"
diff --git a/components/automate-cli/cmd/chef-automate/pullAndGenerateConfig.go b/components/automate-cli/cmd/chef-automate/pullAndGenerateConfig.go
index de3d5a755..a178fedfb 100644
--- a/components/automate-cli/cmd/chef-automate/pullAndGenerateConfig.go
+++ b/components/automate-cli/cmd/chef-automate/pullAndGenerateConfig.go
@@ -197,6 +197,7 @@ type PullConfigs interface {
 	getExceptionIps() []string
 	setExceptionIps(ips []string)
 	getOsCertsByIp(map[string]*ConfigKeys) []CertByIP
+	setInfraAndSSHUtil(*AutomateHAInfraDetails, SSHUtil)
 }
 
 type PullConfigsImpl struct {
@@ -216,6 +217,11 @@ func (p *PullConfigsImpl) getExceptionIps() []string {
 	return p.exceptionIps
 }
 
+func (p *PullConfigsImpl) setInfraAndSSHUtil(infra *AutomateHAInfraDetails, sshUtil SSHUtil) {
+	p.infra = infra
+	p.sshUtil = sshUtil
+}
+
 func (p *PullConfigsImpl) setExceptionIps(ips []string) {
 	p.exceptionIps = ips
 }
diff --git a/components/automate-cli/cmd/chef-automate/sshUtils.go b/components/automate-cli/cmd/chef-automate/sshUtils.go
index a13ae10cb..07f182b26 100644
--- a/components/automate-cli/cmd/chef-automate/sshUtils.go
+++ b/components/automate-cli/cmd/chef-automate/sshUtils.go
@@ -11,15 +11,21 @@ import (
 	"path/filepath"
 	"regexp"
 	"strings"
+	"sync"
 	"time"
 	"unicode"
 
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/sshutils"
+	"github.com/chef/automate/lib/stringutils"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh/knownhosts"
 )
 
+var mutex sync.Mutex
+
 type SSHConfig struct {
 	sshUser    string
 	sshPort    string
@@ -83,12 +89,36 @@ func (s *SSHUtilImpl) getClientConfig() (*ssh.ClientConfig, error) {
 	var (
 		keyErr *knownhosts.KeyError
 	)
+	homePath := os.Getenv("HOME")
+	if homePath == "" {
+		errStr := "Environment variable HOME cannot be empty. Please set a value for HOME env"
+		writer.Errorln(errStr)
+		writer.BufferWriter().Flush()
+		return nil, errors.New(errStr)
+	}
+	sshDirPath := filepath.Join(os.Getenv("HOME"), ".ssh")
+	exists, err := fileutils.PathExists(sshDirPath)
+	if err != nil {
+		errStr := fmt.Sprintf("Error evaluating path: %v\n", err)
+		writer.Errorln(errStr)
+		writer.BufferWriter().Flush()
+		return nil, errors.New(errStr)
+	}
+	if !exists {
+		errStr := fmt.Sprintf("Path %s does not exist", sshDirPath)
+		writer.Errorln(errStr)
+		writer.BufferWriter().Flush()
+		return nil, errors.New(errStr)
+	}
 	// Client config
 	return &ssh.ClientConfig{
 		User: s.SshConfig.sshUser,
 		Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
 		HostKeyCallback: ssh.HostKeyCallback(func(host string, remote net.Addr, pubKey ssh.PublicKey) error {
-			kh := checkKnownHosts()
+			mutex.Lock()
+			defer mutex.Unlock()
+			knownHostPath := filepath.Join(sshDirPath, sshutils.AUTOMATE_KNOWN_HOSTS)
+			kh := checkKnownHosts(knownHostPath)
 			hErr := kh(host, remote, pubKey)
 			// Reference: https://blog.golang.org/go1.13-errors
 			// To understand what errors.As is.
@@ -101,7 +131,7 @@ func (s *SSHUtilImpl) getClientConfig() (*ssh.ClientConfig, error) {
 			} else if errors.As(hErr, &keyErr) && len(keyErr.Want) == 0 {
 				// host key not found in known_hosts then give a warning and continue to connect.
 				// writer.Printf("WARNING: %s is not trusted, adding this key to known_hosts file.\n", host)
-				return addHostKey(host, remote, pubKey)
+				return addHostKey(host, knownHostPath, remote, pubKey)
 			}
 			// writer.Printf("Pub key exists for %s.\n", host)
 			return nil
@@ -129,8 +159,8 @@ func (s *SSHUtilImpl) getConnection() (*ssh.Client, error) {
 	return conn, err
 }
 
-func createKnownHosts() {
-	f, fErr := os.OpenFile(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"), os.O_CREATE, 0600)
+func createKnownHosts(knownHostPath string) {
+	f, fErr := os.OpenFile(knownHostPath, os.O_CREATE, 0600)
 	if fErr != nil {
 		writer.Errorf("%v", fErr)
 		return
@@ -138,9 +168,9 @@ func createKnownHosts() {
 	f.Close()
 }
 
-func checkKnownHosts() ssh.HostKeyCallback {
-	createKnownHosts()
-	kh, e := knownhosts.New(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
+func checkKnownHosts(knownHostPath string) ssh.HostKeyCallback {
+	createKnownHosts(knownHostPath)
+	kh, e := knownhosts.New(knownHostPath)
 	if e != nil {
 		writer.Errorf("%v", e)
 		return nil
@@ -148,10 +178,17 @@ func checkKnownHosts() ssh.HostKeyCallback {
 	return kh
 }
 
-func addHostKey(host string, remote net.Addr, pubKey ssh.PublicKey) error {
+func addHostKey(host, khFilePath string, remote net.Addr, pubKey ssh.PublicKey) error {
+
+	content, err := os.ReadFile(khFilePath)
+	if err != nil {
+		writer.Errorf("Error while reading the known hosts file:%v", err)
+		return err
+	}
+	lastLine := stringutils.GetLastLine(string(content))
+
 	// add host key if host is not found in known_hosts, error object is return, if nil then connection proceeds,
 	// if not nil then connection stops.
-	khFilePath := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
 
 	f, fErr := os.OpenFile(khFilePath, os.O_APPEND|os.O_WRONLY, 0600)
 	if fErr != nil {
@@ -160,9 +197,12 @@ func addHostKey(host string, remote net.Addr, pubKey ssh.PublicKey) error {
 	defer f.Close()
 
 	knownHosts := knownhosts.Normalize(remote.String())
-	_, fileErr := f.WriteString(knownhosts.Line([]string{knownHosts}, pubKey))
-	f.WriteString("\n")
-	return fileErr
+	line := knownhosts.Line([]string{knownHosts}, pubKey)
+	if lastLine != "" {
+		line = "\n" + line
+	}
+	_, err = f.WriteString(line)
+	return err
 }
 
 func (s *SSHUtilImpl) connectAndExecuteCommandOnRemote(remoteCommands string, spinner bool) (string, error) {
diff --git a/components/automate-cli/cmd/chef-automate/status.go b/components/automate-cli/cmd/chef-automate/status.go
index 2018c904c..1d23a8586 100644
--- a/components/automate-cli/cmd/chef-automate/status.go
+++ b/components/automate-cli/cmd/chef-automate/status.go
@@ -56,6 +56,7 @@ type BeStatusValue struct {
 	process     string
 	upTime      string
 	role        string
+	lag         string
 }
 
 type FeStatus []FeStatusValue
@@ -196,7 +197,8 @@ func executeStatusSummary(cmd *cobra.Command, args []string, statusSummaryCmdFla
 		return err
 	}
 	sshUtil := NewSSHUtil(&SSHConfig{})
-	statusSummary := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, statusSummaryCmdFlags, sshUtil)
+	remoteCmdExecutor := NewRemoteCmdExecutorWithoutNodeMap(sshUtil, writer)
+	statusSummary := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, statusSummaryCmdFlags, remoteCmdExecutor)
 	err = statusSummary.Prepare()
 	if err != nil {
 		return err
diff --git a/components/automate-cli/cmd/chef-automate/summary.go b/components/automate-cli/cmd/chef-automate/summary.go
index 1f60b4bda..8863f80c0 100644
--- a/components/automate-cli/cmd/chef-automate/summary.go
+++ b/components/automate-cli/cmd/chef-automate/summary.go
@@ -4,6 +4,7 @@ import (
 	"container/list"
 	"encoding/json"
 	"fmt"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -30,6 +31,7 @@ const (
 	initialHealth               = "ERROR"
 	initialFormattedDuration    = "0d 0h 0m 0s"
 	initialRole                 = "Unknown"
+	initialLag                  = "NA"
 	defaultServiceDetails       = "DefaultServiceDetails"
 	defaultServiceHealthDetails = "DefaultServiceHealthDetails"
 	censusDetails               = "CensusDetails"
@@ -52,7 +54,7 @@ type Summary struct {
 	spinnerTimeout        time.Duration
 	infra                 *AutomateHAInfraDetails
 	statusSummaryCmdFlags *StatusSummaryCmdFlags
-	sshUtil               SSHUtil
+	remoteCmdExecutor     RemoteCmdExecutor
 }
 
 type A2haHabitatAutoTfvars struct {
@@ -60,7 +62,7 @@ type A2haHabitatAutoTfvars struct {
 	HabSupRingKey              string `json:"hab_sup_ring_key"`
 }
 
-func NewStatusSummary(infra *AutomateHAInfraDetails, feStatus FeStatus, beStatus BeStatus, timeout int64, spinnerTimeout time.Duration, flags *StatusSummaryCmdFlags, sshUtil SSHUtil) StatusSummary {
+func NewStatusSummary(infra *AutomateHAInfraDetails, feStatus FeStatus, beStatus BeStatus, timeout int64, spinnerTimeout time.Duration, flags *StatusSummaryCmdFlags, remoteCmdExecutor RemoteCmdExecutor) StatusSummary {
 	return &Summary{
 		feStatus:              feStatus,
 		beStatus:              beStatus,
@@ -68,7 +70,7 @@ func NewStatusSummary(infra *AutomateHAInfraDetails, feStatus FeStatus, beStatus
 		spinnerTimeout:        spinnerTimeout,
 		infra:                 infra,
 		statusSummaryCmdFlags: flags,
-		sshUtil:               sshUtil,
+		remoteCmdExecutor:     remoteCmdExecutor,
 	}
 }
 
@@ -196,6 +198,7 @@ func (ss *Summary) automateNodeMap(automateIps []string) *NodeTypeAndCmd {
 	return automateNodeMap
 }
 
+// returns automateIps, chefServerIps, opensearchIps, postgresqlIps, errList
 func (ss *Summary) getIPAddressesFromFlagOrInfra() ([]string, []string, []string, []string, *list.List) {
 	errorList := list.New()
 	if ss.statusSummaryCmdFlags.node != "" {
@@ -343,8 +346,7 @@ func (ss *Summary) prepareBEScript(serviceIps []string, nodeMap *NodeTypeAndCmd,
 		nodeMap.Postgresql.CmdInputs.MutipleCmdWithArgs = script
 	}
 
-	cmdUtil := NewRemoteCmdExecutor(nodeMap, ss.sshUtil, writer)
-	beOutput, err := cmdUtil.Execute()
+	beOutput, err := ss.remoteCmdExecutor.ExecuteWithNodeMap(nodeMap)
 	if err != nil {
 		return err
 	}
@@ -358,7 +360,6 @@ func (ss *Summary) prepareBEScript(serviceIps []string, nodeMap *NodeTypeAndCmd,
 }
 
 func (ss *Summary) prepareFEScript(serviceIps []string, nodeMap *NodeTypeAndCmd, serviceName, serviceType string) error {
-	cmdUtil := NewRemoteCmdExecutor(nodeMap, ss.sshUtil, writer)
 
 	// Status Script
 	status := GenerateOriginalAutomateCLICommand(
@@ -392,7 +393,7 @@ func (ss *Summary) prepareFEScript(serviceIps []string, nodeMap *NodeTypeAndCmd,
 		nodeMap.ChefServer.CmdInputs.MutipleCmdWithArgs = script
 	}
 
-	feOutput, err := cmdUtil.Execute()
+	feOutput, err := ss.remoteCmdExecutor.ExecuteWithNodeMap(nodeMap)
 	if err != nil {
 		return err
 	}
@@ -431,6 +432,7 @@ func (ss *Summary) getBEStatus(outputs []*CmdResult, ip string, authToken, servi
 		process:     fmt.Sprintf("%s (pid: %s)", initialServiceState, initialServicePid),
 		upTime:      initialFormattedDuration,
 		role:        initialRole,
+		lag:         initialLag,
 	}
 
 	for _, output := range outputs {
@@ -451,6 +453,11 @@ func (ss *Summary) getBEStatus(outputs []*CmdResult, ip string, authToken, servi
 		return defaultBeStatusValue
 	}
 
+	lag, err := ss.getFollowerLag(cmdResMap[defaultServiceHealthDetails].Output, serviceName, role)
+	if err != nil {
+		lag = "Error"
+	}
+
 	return BeStatusValue{
 		serviceName: serviceName,
 		ipAddress:   ip,
@@ -458,6 +465,7 @@ func (ss *Summary) getBEStatus(outputs []*CmdResult, ip string, authToken, servi
 		process:     fmt.Sprintf("%s (pid: %s)", serviceState, servicePid),
 		upTime:      formattedDuration,
 		role:        role,
+		lag:         lag,
 	}
 }
 
@@ -516,6 +524,30 @@ func (ss *Summary) getBECensus(output, service, memeberId string) (string, error
 	return role, nil
 }
 
+func (ss *Summary) getFollowerLag(output, service, role string) (string, error) {
+	lag := initialLag
+	outputMap, err := parseStringInToMapStringInterface(output)
+	if err != nil {
+		return lag, err
+	}
+
+	if service == postgresql {
+		if role == "Follower" {
+			// Define the regular expression pattern to match the substring
+			pattern := `(\d+ bytes and \d+ seconds)`
+
+			// Compile the regular expression pattern
+			regExp := regexp.MustCompile(pattern)
+
+			// Find the first matching substring of the stdout string
+			lag = regExp.FindString(outputMap["stdout"].(string))
+
+			return lag, nil
+		}
+	}
+	return lag, nil
+}
+
 func (ss *Summary) getFEStatus(ip string, outputs []*CmdResult, serviceType string) FeStatusValue {
 	var osStatus, status string
 
@@ -535,12 +567,13 @@ func (ss *Summary) getFEStatus(ip string, outputs []*CmdResult, serviceType stri
 		}
 	}
 
-	return FeStatusValue{
+	feStatusValue := FeStatusValue{
 		serviceName: serviceType,
 		ipAddress:   ip,
 		status:      status,
 		Opensearch:  osStatus,
 	}
+	return feStatusValue
 }
 
 func (ss *Summary) opensearchStatusInFE(osStatusOutput *CmdResult) string {
@@ -630,9 +663,9 @@ func (ss *Summary) ShowBEStatus() string {
 		tTemp := table.Table{}
 		tTemp.Render()
 		for _, status := range ss.beStatus {
-			t.AppendRow(table.Row{status.serviceName, status.ipAddress, status.health, status.process, status.upTime, status.role})
+			t.AppendRow(table.Row{status.serviceName, status.ipAddress, status.health, status.process, status.lag, status.upTime, status.role})
 		}
-		t.AppendHeader(table.Row{"Name", "IP Address", "Health", "Process", "Uptime", "Role"})
+		t.AppendHeader(table.Row{"Name", "IP Address", "Health", "Process", "Lag", "Uptime", "Role"})
 		return t.Render()
 	}
 	return ""
diff --git a/components/automate-cli/cmd/chef-automate/tfvarsToJson.go b/components/automate-cli/cmd/chef-automate/tfvarsToJson.go
index d25e81191..f184df6af 100644
--- a/components/automate-cli/cmd/chef-automate/tfvarsToJson.go
+++ b/components/automate-cli/cmd/chef-automate/tfvarsToJson.go
@@ -12,18 +12,18 @@ import (
 
 func convTfvarToJson(filename string) string {
 	if len(strings.TrimSpace(filename)) < 1 {
-		writer.Printf("Invalid or empty argument")
+		writer.Println("Invalid or empty argument")
 		return ""
 	}
 	src, err := ioutil.ReadFile(filename) // nosemgrep
 	if err != nil {
-		writer.Printf("Error reading %q: %s", filename, err)
+		writer.Printf("Error reading %q: %s\n", filename, err)
 		return ""
 	}
 
 	f, diags := hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1})
 	if diags.HasErrors() {
-		writer.Printf("Error parsing %q: %s", filename, diags.Error())
+		writer.Printf("Error parsing %q: %s\n", filename, diags.Error())
 		return ""
 	}
 	if f.Body == nil {
@@ -33,7 +33,7 @@ func convTfvarToJson(filename string) string {
 
 	attrs, diags := f.Body.JustAttributes()
 	if diags.HasErrors() {
-		writer.Printf("Error evaluating %q: %s", filename, diags.Error())
+		writer.Printf("Error evaluating %q: %s\n", filename, diags.Error())
 		return ""
 	}
 
@@ -41,12 +41,12 @@ func convTfvarToJson(filename string) string {
 	for name, attr := range attrs {
 		value, diags := attr.Expr.Value(nil)
 		if diags.HasErrors() {
-			writer.Printf("Error evaluating %q in %q: %s", name, filename, diags.Error())
+			writer.Printf("Error evaluating %q in %q: %s\n", name, filename, diags.Error())
 			return ""
 		}
 		buf, err := ctyjson.Marshal(value, value.Type())
 		if err != nil {
-			writer.Printf("Error converting %q in %q to JSON: %s", name, filename, err)
+			writer.Printf("Error converting %q in %q to JSON: %s\n", name, filename, err)
 			return ""
 		}
 		values[name] = json.RawMessage(buf)
@@ -54,7 +54,7 @@ func convTfvarToJson(filename string) string {
 
 	output, err := json.MarshalIndent(values, "", "  ")
 	if err != nil {
-		writer.Printf("Error marshalling %q to JSON: %s", filename, err)
+		writer.Printf("Error marshalling %q to JSON: %s\n", filename, err)
 		return ""
 	}
 	return string(output)
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
index aa69b5fee..85e4f3c9f 100644
--- a/components/automate-cli/cmd/chef-automate/verify.go
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -316,7 +316,7 @@ func (v *verifyCmdFlow) RunVerify(config string) error {
 		v.sshPort, v.sshKeyFile, v.sshUserName = v.getSSHConfig(v.Config.Architecture.Aws)
 		configDetails, err := fetchAwsConfigFromTerraform()
 		if err != nil {
-			return status.New(status.ConfigVerifyError, err.Error())
+			return status.Wrap(errors.New("Cannot run verify command before provision in AWS deployment.\nPlease run 'chef-automate provision-infra config.toml --airgap-bundle automate.aib' to provision your infra"), status.ConfigVerifyError, err.Error())
 		}
 
 		v.automateIPs = configDetails.AutomateIps
@@ -712,6 +712,9 @@ func buildReports(batchCheckResults []models.BatchCheckResult) []reporting.Verif
 
 			statusMessage := &reporting.StatusMessage{}
 			statusMessage.MainMessage = fmt.Sprintf("%s - %s", test.Message, info.Status)
+			if test.Skipped {
+				statusMessage.MainMessage = fmt.Sprintf("%s\n%s", statusMessage.MainMessage, test.SkipMessage)
+			}
 			statusMessage.SubMessage = errorMsgs
 			info.StatusMessage = statusMessage
 
diff --git a/components/automate-cli/pkg/verifyserver/constants/checkconstant.go b/components/automate-cli/pkg/verifyserver/constants/checkconstant.go
index 2312ae3f5..45f5d3844 100644
--- a/components/automate-cli/pkg/verifyserver/constants/checkconstant.go
+++ b/components/automate-cli/pkg/verifyserver/constants/checkconstant.go
@@ -28,9 +28,9 @@ const (
 	S3_BACKUP_CONFIG_MSG                = "S3 Backup Config Check"
 	FQDN_MSG                            = "FQDN Check"
 	FIREWALL_MSG                        = "Firewall Check"
-	EXTERNAL_OPENSEARCH_MSG             = "External Opensearch Check"
+	EXTERNAL_OPENSEARCH_MSG             = "External OpenSearch Check"
 	AWS_OPENSEARCH_S3_BUCKET_ACCESS_MSG = "AWS Opensearch S3 Bucket Access Check"
-	EXTERNAL_POSTGRESQL_MSG             = "External Postgresql Check"
+	EXTERNAL_POSTGRESQL_MSG             = "External PostgreSQL Check"
 	NFS_BACKUP_CONFIG_MSG               = "NFS Backup Config Check"
 )
 
diff --git a/components/automate-cli/pkg/verifyserver/constants/skipmessages.go b/components/automate-cli/pkg/verifyserver/constants/skipmessages.go
new file mode 100644
index 000000000..e17def224
--- /dev/null
+++ b/components/automate-cli/pkg/verifyserver/constants/skipmessages.go
@@ -0,0 +1,14 @@
+package constants
+
+const (
+	SKIP_CERT_TEST_MESSAGE                   = "- No Custom certificates provided\n- Using Default certificates"
+	SKIP_CS_FQDN_TEST_MESSAGE                = "- FQDN and LoadBalancer Root CA missing"
+	SKIP_MANAGED_OS_TEST_MESSAGE             = "- No External OpenSearch configuration found\n- Using Chef Managed OpenSearch"
+	SKIP_MANAGED_PG_TEST_MESSAGE             = "- No External OpenSearch configuration found\n- Using Chef Managed PostgreSQL"
+	SKIP_BACKUP_TEST_MESSAGE_NFS             = "- Backup configuration not set to file_system/efs"
+	SKIP_BACKUP_TEST_MESSAGE_S3              = "- Backup configuration not set to object_storage/s3"
+	SKIP_OS_BUCKET_TEST_MESSAGE              = "- No External OpenSearch configuration provided or,\n- No S3 Backup configuration was provided"
+	SKIP_OS_BUCKET_TEST_SELF_MANAGED_MESSAGE = "- External OpenSearch is self managed"
+	SKIP_MISSING_HARDWARE_MESSAGE            = "- Instance count and Instance IPs are missing"
+	SKIP_SSH_USER_TEST_MESSAGE               = "- SSH credentials are missing"
+)
diff --git a/components/automate-cli/pkg/verifyserver/models/batchcheck.go b/components/automate-cli/pkg/verifyserver/models/batchcheck.go
index 52d877c63..d0ba3043a 100644
--- a/components/automate-cli/pkg/verifyserver/models/batchcheck.go
+++ b/components/automate-cli/pkg/verifyserver/models/batchcheck.go
@@ -87,6 +87,7 @@ type ExternalPG struct {
 type Config struct {
 	SSHUser         *SSHUser       `json:"ssh_user"`
 	Arch            string         `json:"arch"`
+	Profile         string         `json:"profile"`
 	Backup          *Backup        `json:"backup"`
 	Hardware        *Hardware      `json:"hardware"`
 	ExternalDbType  string         `json:"external_db_type"`
@@ -280,14 +281,19 @@ func (c *Config) populateAwsS3BucketName(haConfig *config.HaDeployConfig) {
 				BucketName: haConfig.Architecture.Aws.S3BucketName,
 			},
 		}
-		cred := credentials.NewSharedCredentials("", "")
-		creds, err := cred.Get()
-		if err != nil {
-			log.Println("populateAwsS3BucketName:", err)
-		}
-		c.Backup.ObjectStorage.AccessKey = creds.AccessKeyID
-		c.Backup.ObjectStorage.SecretKey = creds.SecretAccessKey
 		c.Backup.ObjectStorage.AWSRegion = haConfig.Aws.Config.Region
+
+		if haConfig.Aws.Config.Profile != "" {
+			cred := credentials.NewSharedCredentials("", "")
+			creds, err := cred.Get()
+			if err != nil {
+				log.Println("populateAwsS3BucketName:", err)
+			}
+
+			c.Backup.ObjectStorage.AccessKey = creds.AccessKeyID
+			c.Backup.ObjectStorage.SecretKey = creds.SecretAccessKey
+		}
+
 	}
 }
 
@@ -466,12 +472,13 @@ type CheckTriggerResponse struct {
 	CheckType string    `json:"check_type"`
 }
 type ApiResult struct {
-	Passed  bool         `json:"passed"`
-	Message string       `json:"msg"`
-	Check   string       `json:"check"`
-	Checks  []Checks     `json:"checks"`
-	Error   *fiber.Error `json:"error,omitempty"`
-	Skipped bool         `json:"skipped"`
+	Passed      bool         `json:"passed"`
+	Message     string       `json:"msg"`
+	Check       string       `json:"check"`
+	Checks      []Checks     `json:"checks"`
+	Error       *fiber.Error `json:"error,omitempty"`
+	Skipped     bool         `json:"skipped"`
+	SkipMessage string       `json:"skip_message,omitempty"`
 }
 
 type Checks struct {
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger.go
index 6ee5522a8..6bc162d71 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger.go
@@ -30,7 +30,7 @@ func (ss *CertificateCheck) Run(config *models.Config) []models.CheckTriggerResp
 
 	// Check if certificate is empty or nil
 	if isCertificateNil(config.Certificate) {
-		return trigger.ConstructNilResp(config, constants.CERTIFICATE)
+		return trigger.ConstructNilResp(config, constants.CERTIFICATE, constants.SKIP_CERT_TEST_MESSAGE)
 	}
 	if isCertificateEmpty(config.Certificate) {
 		return trigger.ConstructEmptyResp(config, constants.CERTIFICATE, constants.MISSING_CERTIFICATE)
@@ -95,7 +95,7 @@ func skipCertificateForAutomateAndChefServerNodes(nodeType string, hardwareMap m
 	for ip, nodeTypes := range hardwareMap {
 		for _, nodeTypeMap := range nodeTypes {
 			if nodeTypeMap == nodeType {
-				result = append(result, trigger.SkippedTriggerCheckResp(ip, constants.CERTIFICATE, nodeTypeMap))
+				result = append(result, trigger.SkippedTriggerCheckResp(ip, constants.CERTIFICATE, nodeTypeMap, constants.SKIP_CERT_TEST_MESSAGE))
 			}
 		}
 	}
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchcheck.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchcheck.go
index 6e869c335..19cd8a6ad 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchcheck.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchcheck.go
@@ -25,10 +25,10 @@ func NewExternalOpensearchCheck(log logger.Logger, port string) *ExternalOpensea
 func (eoc *ExternalOpensearchCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	// Check for nil or empty req body
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.EXTERNAL_OPENSEARCH, false, false, false)
+		return trigger.HardwareNil(constants.EXTERNAL_OPENSEARCH, constants.SKIP_MISSING_HARDWARE_MESSAGE, false, false, false)
 	}
 	if config.ExternalOS == nil {
-		return externalOSNillResp(config, constants.EXTERNAL_OPENSEARCH)
+		return externalOSNillResp(config, constants.EXTERNAL_OPENSEARCH, constants.SKIP_MANAGED_OS_TEST_MESSAGE)
 	}
 
 	if isEmptyExternalOS(config.ExternalOS) {
@@ -85,15 +85,15 @@ func getOpensearchRequest(details *models.ExternalOS) models.ExternalOSRequest {
 
 }
 
-func externalOSNillResp(config *models.Config, checktype string) []models.CheckTriggerResponse {
+func externalOSNillResp(config *models.Config, checktype, message string) []models.CheckTriggerResponse {
 	var triggerResps []models.CheckTriggerResponse
 
 	for _, ip := range config.Hardware.AutomateNodeIps {
-		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.AUTOMATE))
+		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.AUTOMATE, message))
 	}
 
 	for _, ip := range config.Hardware.ChefInfraServerNodeIps {
-		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.CHEF_INFRA_SERVER))
+		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.CHEF_INFRA_SERVER, message))
 	}
 
 	return triggerResps
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlcheck.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlcheck.go
index f3dd618b9..b3148d010 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlcheck.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlcheck.go
@@ -26,10 +26,10 @@ func NewExternalPostgresCheck(log logger.Logger, port string) *ExternalPostgresC
 func (epc *ExternalPostgresCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	// Check for nil or empty req body
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.EXTERNAL_POSTGRESQL, false, false, false)
+		return trigger.HardwareNil(constants.EXTERNAL_POSTGRESQL, constants.SKIP_MISSING_HARDWARE_MESSAGE, false, false, false)
 	}
 	if config.ExternalPG == nil {
-		return externalPGNillResp(config, constants.EXTERNAL_POSTGRESQL)
+		return externalPGNillResp(config, constants.EXTERNAL_POSTGRESQL, constants.SKIP_MANAGED_PG_TEST_MESSAGE)
 	}
 	if isEmptyExternalPG(config.ExternalPG) {
 		return externalPGEmptyResp(config, constants.EXTERNAL_POSTGRESQL)
@@ -87,15 +87,15 @@ func getPostgresRequest(details *models.ExternalPG) models.ExternalPgRequest {
 	}
 }
 
-func externalPGNillResp(config *models.Config, checkType string) []models.CheckTriggerResponse {
+func externalPGNillResp(config *models.Config, checkType, message string) []models.CheckTriggerResponse {
 	var triggerResps []models.CheckTriggerResponse
 
 	for _, ip := range config.Hardware.AutomateNodeIps {
-		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.AUTOMATE))
+		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.AUTOMATE, message))
 	}
 
 	for _, ip := range config.Hardware.ChefInfraServerNodeIps {
-		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.CHEF_INFRA_SERVER))
+		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.CHEF_INFRA_SERVER, message))
 	}
 
 	return triggerResps
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 1b56935ec..2db984fba 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
@@ -32,7 +32,7 @@ func (fc *FirewallCheck) Run(config *models.Config) []models.CheckTriggerRespons
 	fc.log.Info("Performing Firewall check from batch check ")
 
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.FIREWALL, true, true, true)
+		return trigger.HardwareNil(constants.FIREWALL, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, true, true)
 	}
 
 	requestMap := make(map[string][]models.FirewallRequest)
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck.go
index 1bcf68f38..93c69409f 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck.go
@@ -30,11 +30,11 @@ func (fqc *FqdnCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	endPoint := checkutils.PrepareEndPoint(fqc.host, fqc.port, constants.FQDN_LOAD_BALANCER_CHECK)
 
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.FQDN, false, false, false)
+		return trigger.HardwareNil(constants.FQDN, constants.SKIP_MISSING_HARDWARE_MESSAGE, false, false, false)
 	}
 
 	if config.Certificate == nil {
-		return constructNilResp(config, constants.FQDN)
+		return constructNilResp(config, constants.FQDN, constants.SKIP_CS_FQDN_TEST_MESSAGE)
 	}
 
 	return triggerFqdnCheck(config, endPoint, fqc.log)
@@ -114,13 +114,13 @@ func (ss *FqdnCheck) GetPortsForMockServer() map[string]map[string][]int {
 	return nodeTypePortMap
 }
 
-func constructNilResp(config *models.Config, checktype string) []models.CheckTriggerResponse {
+func constructNilResp(config *models.Config, checktype, message string) []models.CheckTriggerResponse {
 	resps := []models.CheckTriggerResponse{}
 	for _, ip := range config.Hardware.AutomateNodeIps {
-		resps = append(resps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.AUTOMATE))
+		resps = append(resps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.AUTOMATE, message))
 	}
 	for _, ip := range config.Hardware.ChefInfraServerNodeIps {
-		resps = append(resps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.CHEF_INFRA_SERVER))
+		resps = append(resps, trigger.SkippedTriggerCheckResp(ip, checktype, constants.CHEF_INFRA_SERVER, message))
 	}
 
 	return resps
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 593aa285b..85d16287b 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
@@ -32,10 +32,10 @@ func NewNfsBackupConfigCheck(log logger.Logger, port string) *NfsBackupConfigChe
 
 func (nbc *NfsBackupConfigCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.NFS_BACKUP_CONFIG, true, true, false)
+		return trigger.HardwareNil(constants.NFS_BACKUP_CONFIG, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, true, false)
 	}
 	if config.Backup == nil || config.Backup.FileSystem == nil {
-		return trigger.ConstructNilResp(config, constants.NFS_BACKUP_CONFIG)
+		return trigger.ConstructNilResp(config, constants.NFS_BACKUP_CONFIG, constants.SKIP_BACKUP_TEST_MESSAGE_NFS)
 	}
 	if isMountLocationEmpty(config.Backup) {
 		return trigger.ConstructEmptyResp(config, constants.NFS_BACKUP_CONFIG, constants.MOUNT_LOCATION_MISSING)
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 4e03c8c65..87a520505 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
@@ -29,12 +29,12 @@ func NewOpensearchS3BucketAccessCheck(log logger.Logger, port string) *Opensearc
 func (osb *OpensearchS3BucketAccessCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	if config.ExternalOS == nil || config.Backup == nil || config.Backup.ObjectStorage == nil {
 		return []models.CheckTriggerResponse{
-			trigger.SkippedTriggerCheckResp("-", constants.AWS_OPENSEARCH_S3_BUCKET_ACCESS, constants.OPENSEARCH),
+			trigger.SkippedTriggerCheckResp("-", constants.AWS_OPENSEARCH_S3_BUCKET_ACCESS, constants.OPENSEARCH, constants.SKIP_OS_BUCKET_TEST_MESSAGE),
 		}
 	}
 	if config.ExternalDbType == cfg.SELF_MANAGED {
 		return []models.CheckTriggerResponse{
-			trigger.SkippedTriggerCheckResp(config.ExternalOS.OSDomainURL, constants.AWS_OPENSEARCH_S3_BUCKET_ACCESS, constants.OPENSEARCH),
+			trigger.SkippedTriggerCheckResp(config.ExternalOS.OSDomainURL, constants.AWS_OPENSEARCH_S3_BUCKET_ACCESS, constants.OPENSEARCH, constants.SKIP_OS_BUCKET_TEST_SELF_MANAGED_MESSAGE),
 		}
 	}
 	if isEmptyExternalOS(config.ExternalOS) || isObjectStorage(config.Backup) {
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger.go
index 4c27ea2e6..204237768 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger.go
@@ -25,15 +25,15 @@ func NewS3BackupConfigCheck(log logger.Logger, port string) *S3BackupConfigCheck
 func (svc *S3BackupConfigCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	if config.Hardware == nil {
 		return []models.CheckTriggerResponse{
-			trigger.SkippedTriggerCheckResp("-", constants.S3_BACKUP_CONFIG, constants.AUTOMATE),
+			trigger.SkippedTriggerCheckResp("-", constants.S3_BACKUP_CONFIG, constants.AUTOMATE, constants.SKIP_MISSING_HARDWARE_MESSAGE),
 		}
 	}
 
 	if config.Backup == nil || config.Backup.ObjectStorage == nil {
-		return s3ConfigSkippedResponse(config, constants.S3_BACKUP_CONFIG)
+		return s3ConfigSkippedResponse(config, constants.S3_BACKUP_CONFIG, constants.SKIP_BACKUP_TEST_MESSAGE_S3)
 	}
 
-	if isObjectStorage(config.Backup) {
+	if !isObjectStorage(config ) {
 		return emptyResp(config, constants.S3_BACKUP_CONFIG)
 	}
 
@@ -41,15 +41,15 @@ func (svc *S3BackupConfigCheck) Run(config *models.Config) []models.CheckTrigger
 	return runCheckForS3Config(config.Hardware.AutomateNodeIps, svc.log, svc.port, http.MethodPost, req)
 }
 
-func s3ConfigSkippedResponse(config *models.Config, checkType string) []models.CheckTriggerResponse {
+func s3ConfigSkippedResponse(config *models.Config, checkType, message string) []models.CheckTriggerResponse {
 	var triggerResps []models.CheckTriggerResponse
 
 	for _, ip := range config.Hardware.AutomateNodeIps {
-		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.AUTOMATE))
+		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.AUTOMATE, message))
 	}
 
 	for _, ip := range config.Hardware.ChefInfraServerNodeIps {
-		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.CHEF_INFRA_SERVER))
+		triggerResps = append(triggerResps, trigger.SkippedTriggerCheckResp(ip, checkType, constants.CHEF_INFRA_SERVER, message))
 	}
 
 	return triggerResps
@@ -102,10 +102,17 @@ func getS3CheckRequest(object *models.ObjectStorage) models.S3ConfigRequest {
 	}
 }
 
-func isObjectStorage(backup *models.Backup) bool {
-	return backup.ObjectStorage.BucketName == "" ||
-		backup.ObjectStorage.AccessKey == "" ||
-		backup.ObjectStorage.SecretKey == ""
+func isObjectStorage(config *models.Config) bool {
+	backup := config.Backup
+	if backup.ObjectStorage.BucketName == "" {
+		return false
+	} else if config.Profile != "" {
+		if backup.ObjectStorage.AccessKey == "" ||
+			backup.ObjectStorage.SecretKey == "" {
+			return false
+		}
+	}
+	return true
 }
 
 func emptyResp(config *models.Config, checktype string) []models.CheckTriggerResponse {
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/softwareversionchecktrigger/softwareversionchecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/softwareversionchecktrigger/softwareversionchecktrigger.go
index 2fa2ab4d3..1a8d6d6ab 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/softwareversionchecktrigger/softwareversionchecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/softwareversionchecktrigger/softwareversionchecktrigger.go
@@ -22,7 +22,7 @@ func NewSoftwareVersionCheck(log logger.Logger, port string) *SoftwareVersionChe
 func (svc *SoftwareVersionCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	// Check for config.HardWare if empty of nil
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.SOFTWARE_VERSIONS, true, true, true)
+		return trigger.HardwareNil(constants.SOFTWARE_VERSIONS, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, true, true)
 	}
 
 	return trigger.RunCheck(config, svc.log, svc.port, constants.SOFTWARE_VERSION_CHECK_API_PATH, "")
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go
index 6018e3c6e..1bcaf1b5a 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go
@@ -35,10 +35,10 @@ func (ss *SshUserAccessCheck) Run(config *models.Config) []models.CheckTriggerRe
 
 	// Check if certificate is empty or nil
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.SSH_USER, true, true, false)
+		return trigger.HardwareNil(constants.SSH_USER, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, true, false)
 	}
 	if config.SSHUser == nil {
-		return trigger.ConstructNilResp(config, constants.SSH_USER)
+		return trigger.ConstructNilResp(config, constants.SSH_USER, constants.SKIP_SSH_USER_TEST_MESSAGE)
 	}
 	if IsSSHUserEmpty(config.SSHUser) {
 		return trigger.ConstructEmptyResp(config, constants.SSH_USER, "SSH credentials is missing")
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemresourcechecktrigger/systemresourceschecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemresourcechecktrigger/systemresourceschecktrigger.go
index 32df834d5..9fc9e311a 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemresourcechecktrigger/systemresourceschecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemresourcechecktrigger/systemresourceschecktrigger.go
@@ -22,7 +22,7 @@ func NewSystemResourceCheck(log logger.Logger, port string) *SystemResourceCheck
 func (src *SystemResourceCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	// Check for config.HardWare if empty of nil
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.SYSTEM_RESOURCES, true, true, true)
+		return trigger.HardwareNil(constants.SYSTEM_RESOURCES, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, true, true)
 	}
 
 	return trigger.RunCheck(config, src.log, src.port, constants.SYSTEM_RESOURCE_CHECK_API_PATH, config.DeploymentState)
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemuserchecktrigger/systemuserchecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemuserchecktrigger/systemuserchecktrigger.go
index 2f112abc1..be3330eea 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemuserchecktrigger/systemuserchecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/systemuserchecktrigger/systemuserchecktrigger.go
@@ -22,7 +22,7 @@ func NewSystemUserCheck(log logger.Logger, port string) *SystemUserCheck {
 func (suc *SystemUserCheck) Run(config *models.Config) []models.CheckTriggerResponse {
 	// Check for config.HardWare if empty of nil
 	if config.Hardware == nil {
-		return trigger.HardwareNil(constants.SYSTEM_USER, true, true, true)
+		return trigger.HardwareNil(constants.SYSTEM_USER, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, true, true)
 	}
 
 	return trigger.RunCheck(config, suc.log, suc.port, constants.SYSTEM_USER_CHECK_API_PATH, "")
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi.go
index 6302c32ef..5b4590b10 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi.go
@@ -221,15 +221,16 @@ func interfaceToIOReader(body interface{}) (io.Reader, error) {
 	return reader, nil
 }
 
-func HardwareNil(checkType string, includeOPENSEARCH bool, includePOSTGRESQL bool, includeBASTION bool) []models.CheckTriggerResponse {
+func HardwareNil(checkType, message string, includeOPENSEARCH bool, includePOSTGRESQL bool, includeBASTION bool) []models.CheckTriggerResponse {
 	responses := []models.CheckTriggerResponse{
 		{
 			NodeType:  constants.AUTOMATE,
 			CheckType: checkType,
 			Result: models.ApiResult{
-				Passed:  false,
-				Skipped: true,
-				Check:   checkType,
+				Passed:      false,
+				Skipped:     true,
+				SkipMessage: message,
+				Check:       checkType,
 			},
 			Host: constants.UNKNOWN_HOST,
 		},
@@ -237,9 +238,10 @@ func HardwareNil(checkType string, includeOPENSEARCH bool, includePOSTGRESQL boo
 			NodeType:  constants.CHEF_INFRA_SERVER,
 			CheckType: checkType,
 			Result: models.ApiResult{
-				Passed:  false,
-				Skipped: true,
-				Check:   checkType,
+				Passed:      false,
+				Skipped:     true,
+				SkipMessage: message,
+				Check:       checkType,
 			},
 			Host: constants.UNKNOWN_HOST,
 		},
@@ -250,9 +252,10 @@ func HardwareNil(checkType string, includeOPENSEARCH bool, includePOSTGRESQL boo
 			NodeType:  constants.OPENSEARCH,
 			CheckType: checkType,
 			Result: models.ApiResult{
-				Passed:  false,
-				Skipped: true,
-				Check:   checkType,
+				Passed:      false,
+				Skipped:     true,
+				SkipMessage: message,
+				Check:       checkType,
 			},
 			Host: constants.UNKNOWN_HOST,
 		})
@@ -263,9 +266,10 @@ func HardwareNil(checkType string, includeOPENSEARCH bool, includePOSTGRESQL boo
 			NodeType:  constants.POSTGRESQL,
 			CheckType: checkType,
 			Result: models.ApiResult{
-				Passed:  false,
-				Skipped: true,
-				Check:   checkType,
+				Passed:      false,
+				Skipped:     true,
+				SkipMessage: message,
+				Check:       checkType,
 			},
 			Host: constants.UNKNOWN_HOST,
 		})
@@ -276,9 +280,10 @@ func HardwareNil(checkType string, includeOPENSEARCH bool, includePOSTGRESQL boo
 			NodeType:  constants.BASTION,
 			CheckType: checkType,
 			Result: models.ApiResult{
-				Passed:  false,
-				Skipped: true,
-				Check:   checkType,
+				Passed:      false,
+				Skipped:     true,
+				SkipMessage: message,
+				Check:       checkType,
 			},
 			Host: constants.LOCALHOST,
 		})
@@ -303,14 +308,15 @@ func ErrTriggerCheckResp(ip, checkType, nodeType, msg string) models.CheckTrigge
 	}
 }
 
-func SkippedTriggerCheckResp(ip, checktype, nodeType string) models.CheckTriggerResponse {
+func SkippedTriggerCheckResp(ip, checktype, nodeType, message string) models.CheckTriggerResponse {
 	return models.CheckTriggerResponse{
 		NodeType:  nodeType,
 		CheckType: checktype,
 		Result: models.ApiResult{
-			Passed:  false,
-			Skipped: true,
-			Check:   checktype,
+			Passed:      false,
+			Skipped:     true,
+			Check:       checktype,
+			SkipMessage: message,
 		},
 		Host: ip,
 	}
@@ -334,19 +340,19 @@ func ConstructEmptyResp(config *models.Config, checktype, msg string) []models.C
 	return resps
 }
 
-func ConstructNilResp(config *models.Config, checktype string) []models.CheckTriggerResponse {
+func ConstructNilResp(config *models.Config, checktype, message string) []models.CheckTriggerResponse {
 	resps := []models.CheckTriggerResponse{}
 	for _, ip := range config.Hardware.AutomateNodeIps {
-		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.AUTOMATE))
+		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.AUTOMATE, message))
 	}
 	for _, ip := range config.Hardware.ChefInfraServerNodeIps {
-		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.CHEF_INFRA_SERVER))
+		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.CHEF_INFRA_SERVER, message))
 	}
 	for _, ip := range config.Hardware.PostgresqlNodeIps {
-		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.POSTGRESQL))
+		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.POSTGRESQL, message))
 	}
 	for _, ip := range config.Hardware.OpenSearchNodeIps {
-		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.OPENSEARCH))
+		resps = append(resps, SkippedTriggerCheckResp(ip, checktype, constants.OPENSEARCH, message))
 	}
 
 	return resps
diff --git a/components/automate-cli/pkg/verifyserver/utils/awsutils/aws.go b/components/automate-cli/pkg/verifyserver/utils/awsutils/aws.go
index 54884c868..1a2b11c91 100644
--- a/components/automate-cli/pkg/verifyserver/utils/awsutils/aws.go
+++ b/components/automate-cli/pkg/verifyserver/utils/awsutils/aws.go
@@ -35,7 +35,9 @@ func (au *AwsUtilsImpl) NewSessionWithOptions(endpoint, accessKey, secretKey, re
 	}
 	config.Endpoint = &endpoint
 	config.Region = aws.String(region)
-	config.Credentials = credentials.NewStaticCredentials(accessKey, secretKey, "")
+	if accessKey != "" && secretKey != "" {
+		config.Credentials = credentials.NewStaticCredentials(accessKey, secretKey, "")
+	}
 	return session.NewSessionWithOptions(session.Options{
 		Config: config,
 	})
diff --git a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
index 16a833b78..d6e8d2c98 100644
--- a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
+++ b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
@@ -32,13 +32,14 @@ Environment="HOME={{.HomeEnv}}"
 [Install]
 WantedBy=multi-user.target
 `
+	BINARY_FILE_NAME = "chef-automate"
 )
 
 type systemdInput struct {
 	ServiceName        string
 	ServiceDescription string
 	ServiceCommand     string
-	HomeEnv            string  
+	HomeEnv            string
 }
 
 type CreateSystemdServiceImpl struct {
@@ -204,7 +205,7 @@ func (css *CreateSystemdServiceImpl) Create() error {
 	}
 	css.Logger.Debugf("Service %s is not running on this system", SERVICE_NAME)
 
-	fullBinaryDestination := filepath.Join(css.BinaryDestinationFolder, filepath.Base(currentBinaryPath))
+	fullBinaryDestination := filepath.Join(css.BinaryDestinationFolder, BINARY_FILE_NAME)
 	css.Logger.Debugf("Full binary destination path %s", fullBinaryDestination)
 	err = css.SystemdCreateUtils.CreateDestinationAndCopy(currentBinaryPath, fullBinaryDestination)
 	if err != nil {
diff --git a/components/docs-chef-io/Makefile b/components/docs-chef-io/Makefile
index d721e2183..8aa118322 100644
--- a/components/docs-chef-io/Makefile
+++ b/components/docs-chef-io/Makefile
@@ -7,6 +7,7 @@ SWAGGER_FILES=$(shell find $(SWAGGER_DIR) -name '*.swagger.json')
 STATIC_SWAGGER_FILES=$(shell find data/automate/api-static/ -type f | sort -n)
 
 preview_netlify: chef_web_docs
+	rm -rf chef-web-docs/_vendor/github.com/chef/automate/docs-chef-io/*
 	cp -R content/* chef-web-docs/_vendor/github.com/chef/automate/components/docs-chef-io/content
 	cp -R data/* chef-web-docs/_vendor/github.com/chef/automate/components/docs-chef-io/data
 	cp -R layouts/* chef-web-docs/_vendor/github.com/chef/automate/components/docs-chef-io/layouts
diff --git a/components/docs-chef-io/content/automate/backup.md b/components/docs-chef-io/content/automate/backup.md
index adf34f6d4..f88185019 100644
--- a/components/docs-chef-io/content/automate/backup.md
+++ b/components/docs-chef-io/content/automate/backup.md
@@ -181,7 +181,7 @@ See how to [restore from GCS]({{< ref "restore/#restore-from-a-google-cloud-stor
 
 ### Create a Backup
 
-Make a backup with the [`backup create`]({{< ref "cli_chef_automate/#chef-automate-backup-create" >}}) command:
+Make a backup with the [`backup create`]({{< relref "cli/#chef-automate-backup-create" >}}) command:
 
 ```shell
 chef-automate backup create
@@ -195,7 +195,7 @@ Success: Created backup 20180518010336
 ```
 
 Restores from a filesystem backup may fail with incorrect directory permissions.
-Run the [`fix-repo-permissions` command]({{< ref "cli_chef_automate/#chef-automate-backup-fix-repo-permissions" >}}) to address such issues:
+Run the [`fix-repo-permissions` command]({{< relref "cli/#chef-automate-backup-fix-repo-permissions" >}}) to address such issues:
 
 ```shell
 sudo chef-automate backup fix-repo-permissions <path>
@@ -203,7 +203,7 @@ sudo chef-automate backup fix-repo-permissions <path>
 
 ### List Backups
 
-You can list existing backups with the [`backup list`]({{< ref "cli_chef_automate/#chef-automate-backup-list" >}}) command:
+You can list existing backups with the [`backup list`]({{< relref "cli/#chef-automate-backup-list" >}}) command:
 
 ```shell
 chef-automate backup list
@@ -250,7 +250,7 @@ where `bucket_name` is the name of the GCS bucket and `base_path` is an optional
 
 ## Delete Backups
 
-To delete backups from a running instance of Chef Automate, first find the relevant backup ID with `chef-automate backup list` and then delete the backup using [`chef automate backup delete ID`]({{< ref "cli_chef_automate/#chef-automate-backup-delete" >}}).
+To delete backups from a running instance of Chef Automate, first find the relevant backup ID with `chef-automate backup list` and then delete the backup using [`chef automate backup delete ID`]({{< relref "cli/#chef-automate-backup-delete" >}}).
 
 ```shell
 chef-automate backup list
@@ -300,4 +300,4 @@ chef-automate debug set-log-level deployment-service debug
 
 ## References
 
-See the [`chef-automate backup` command reference]({{< ref "cli_chef_automate/#chef-automate-backup" >}}).
+See the [`chef-automate backup` command reference]({{< relref "cli/#chef-automate-backup" >}}).
diff --git a/components/docs-chef-io/content/automate/cli_chef_automate.md b/components/docs-chef-io/content/automate/cli.md
similarity index 77%
rename from components/docs-chef-io/content/automate/cli_chef_automate.md
rename to components/docs-chef-io/content/automate/cli.md
index 4346a62ae..233c00260 100644
--- a/components/docs-chef-io/content/automate/cli_chef_automate.md
+++ b/components/docs-chef-io/content/automate/cli.md
@@ -1,7 +1,7 @@
 +++
-title = "Automate CLI (Standalone)"
+title = "Chef Automate CLI"
+aliases = ['cli_chef_automate', 'cli_chef_automate_ha']
 
-date = 2018-03-26T15:29:24-07:00
 draft = false
 
 gh_repo = "automate"
@@ -9,13 +9,13 @@ toc_layout = "cli_chef_automate_toc"
 
 [menu]
   [menu.automate]
-    title = "Automate CLI (Standalone)"
+    title = "Automate CLI"
     parent = "automate/reference"
-    identifier = "automate/reference/cli_chef_automate.md Automate CLI (Standalone)"
+    identifier = "automate/reference/cli_chef_automate.md Automate CLI"
     weight = 24
 +++
 
-## Chef Automate CLI Commands
+## `chef-automate` CLI Commands
 
 {{< automate/automate_cli_commands data_path="automate/cli_chef_automate/commands" >}}
 
diff --git a/components/docs-chef-io/content/automate/cli_chef_automate_ha.md b/components/docs-chef-io/content/automate/cli_chef_automate_ha.md
deleted file mode 100644
index 5f3cad903..000000000
--- a/components/docs-chef-io/content/automate/cli_chef_automate_ha.md
+++ /dev/null
@@ -1,27 +0,0 @@
-+++
-title = "Automate CLI (HA)"
-
-date = 2018-03-26T15:29:24-07:00
-draft = false
-
-gh_repo = "automate"
-toc_layout = "cli_chef_automate_toc"
-
-[menu]
-  [menu.automate]
-    title = "Automate CLI (HA)"
-    parent = "automate/reference"
-    identifier = "automate/reference/cli_chef_automate_ha.md Automate CLI (HA)"
-    weight = 25
-+++
-
-## Chef Automate CLI Commands
-
-{{< automate/automate_cli_commands_ha data_path="automate/cli_chef_automate/commands" >}}
-
-## Error Codes 
-
-If `chef-automate` encounters an error during execution, it exits with a non-zero
-error code. Here's what our error codes mean:
-
-{{< automate/automate_cli_status_errors data_path="automate/cli_chef_automate/status/errors" >}}
diff --git a/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md b/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md
index caf8f8530..fa2c38346 100644
--- a/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md
+++ b/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md
@@ -110,7 +110,7 @@ Run the following steps on Bastion Host Machine:
     - If you don't have a custom FQDN leave `fqdn` as empty for this AWS deployment. By default, AWS Application load balancer will be used as `fqdn`.
     - Set `instance_count` for Chef Automate, Chef Infra Server, Postgresql, OpenSearch.
     - Set AWS Config Details:
-        - Set `profile`, by default `profile` is `"default"`
+        - Set the AWS `profile`. In case you attached the IAM role to the bastion machine, leave this field empty.
         - Set `region`, by default `region` is `"us-east-1"`
         - Set `aws_vpc_id`, which you had created as Prerequisite step. Example: `"vpc12318h"`
         - Set `private_custom_subnets` to the 3 private subnets we created in prerequisites step and set `public_custom_subnets` to the 3 public subnets we created in prerequisites step: example : `["subnet-07e469d218301533","subnet-07e469d218041534","subnet-07e469d283041535"]`
@@ -161,7 +161,8 @@ Run the following steps on Bastion Host Machine:
     ```
 
 1. After the deployment successfully completed. To view the automate UI, run the command `chef-automate info`, you will get the `automate_url`.
-   If we want to change the FQDN URL from the loadbalancer URL to some other FQDN URL, then use below template
+  If you want to change the FQDN URL from the loadbalancer URL to some other FQDN URL, then use below template
+
 
 -   create a file `a2.fqdn.toml`
 
diff --git a/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md b/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md
index e5665cb98..bea25bf6b 100644
--- a/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md
+++ b/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md
@@ -110,7 +110,7 @@ Set the above prerequisites in `~/.aws/credentials` in Bastion Host:
    - Set `backup_config` to `"s3"` or `efs`.
    - If `backup_config` is `s3`, uncomment and set the value for following `s3_bucketName` attribute to your bucket name. If the bucket name does not exist, it will be created for you automatically.
    - Set `admin_password` to access Chef Automate UI for user `admin`.
-   - Don't set `fqdn` for the AWS deployment.
+   - If you don't have a custom FQDN leave `fqdn` as empty for this AWS deployment. By default, AWS Application load balancer will be used as `fqdn`.
    - Set `instance_count` for *Chef Automate*, *Chef Infra Server*, *Postgresql*, *OpenSearch*.
    - Set AWS Config Details:
       - Set `profile`. The default value of `profile` is `"default"`.
@@ -153,6 +153,13 @@ Set the above prerequisites in `~/.aws/credentials` in Bastion Host:
 
    #Run provision command to deploy `automate.aib` with set `config.toml`
    chef-automate provision-infra config.toml --airgap-bundle automate.aib
+    "
+    ```
+
+1. Once the provisioning is successful, **if you have added custom DNS to your configuration file (`fqdn`), make sure to map the load-balancer FQDN from the output of previous command to your DNS from DNS Provider**. After that continue with the deployment process with following.
+
+    ```bash
+    sudo -- sh -c "
 
    #Run deploy command to deploy `automate.aib` with set `config.toml`
    chef-automate deploy config.toml --airgap-bundle automate.aib
@@ -166,7 +173,7 @@ Set the above prerequisites in `~/.aws/credentials` in Bastion Host:
    ```
 
 1. After the deployment successfully completed. To view the automate UI, run the command `chef-automate info`, you will get the `automate_url`.
-  If we want to change the FQDN URL from the loadbalancer URL to some other FQDN URL, then use below template
+  If you want to change the FQDN URL from the loadbalancer URL to some other FQDN URL, then use below template
   
 - Create a file `a2.fqdn.toml`
 
diff --git a/components/docs-chef-io/content/automate/ha_chef_backend_to_automate_ha.md b/components/docs-chef-io/content/automate/ha_chef_backend_to_automate_ha.md
index 045f17477..2d85b4a05 100644
--- a/components/docs-chef-io/content/automate/ha_chef_backend_to_automate_ha.md
+++ b/components/docs-chef-io/content/automate/ha_chef_backend_to_automate_ha.md
@@ -309,4 +309,4 @@ Bootstrap the nodes to update the `chef_server_url` using the following steps:
 
 ## Use Automate HA for Chef-Backend User
 
-Download and Install the [Chef Workstation](https://www.chef.io/downloads/tools/workstation) from the Bastion machine or local machine install chef-workstation. You can refer to the [Workstation page](https://docs.chef.io/workstation/getting_started/#set-up-your-chef-repo) to set up your Workstation.
+From the bastion host or a local machine, download and install [Chef Workstation](https://www.chef.io/downloads). See the [Chef Workstation documentation](/workstation/getting_started/#set-up-your-chef-repo) for information on setting up Workstation.
diff --git a/components/docs-chef-io/content/automate/managed_services.md b/components/docs-chef-io/content/automate/managed_services.md
index 2284ca2f8..26a061df3 100644
--- a/components/docs-chef-io/content/automate/managed_services.md
+++ b/components/docs-chef-io/content/automate/managed_services.md
@@ -191,7 +191,7 @@ Before switching to external AWS PostgreSQL RDS/OpenSearch services, take s3 bac
 
 ### Create a Backup
 
-Make a backup with the [backup create]({{< ref "cli_chef_automate/#chef-automate-backup-create" >}}) command:
+Make a backup with the [backup create]({{< relref "cli/#chef-automate-backup-create" >}}) command:
 
 ```shell
 chef-automate backup create
@@ -205,7 +205,7 @@ Success: Created backup 20180518010336
 
 ### List Backups
 
-You can list existing backups with the [backup list]({{< ref "cli_chef_automate/#chef-automate-backup-list" >}}) command:
+You can list existing backups with the [backup list]({{< relref "cli/#chef-automate-backup-list" >}}) command:
 
 ```shell
 chef-automate backup list
@@ -610,7 +610,7 @@ When your Chef Automate instance is equipped with internet connectivity, you can
 
 ### Create a Backup
 
-Make a backup with the [`backup create`]({{< ref "cli_chef_automate/#chef-automate-backup-create" >}}) command:
+Make a backup with the [`backup create`]({{< relref "cli/#chef-automate-backup-create" >}}) command:
 
 ```shell
 chef-automate backup create
@@ -625,7 +625,7 @@ Success: Created backup 20180518010336
 
 ### List Backups
 
-You can list existing backups with the [`backup list`]({{< ref "cli_chef_automate/#chef-automate-backup-list" >}}) command:
+You can list existing backups with the [`backup list`]({{< relref "cli/#chef-automate-backup-list" >}}) command:
 
 ```shell
 chef-automate backup list
diff --git a/components/docs-chef-io/content/automate/restore.md b/components/docs-chef-io/content/automate/restore.md
index 782ce684a..d1ca6e284 100644
--- a/components/docs-chef-io/content/automate/restore.md
+++ b/components/docs-chef-io/content/automate/restore.md
@@ -100,7 +100,7 @@ chef-automate backup restore </path/to/backups/>BACKUP_ID --patch-config </path/
 ```
 
 Restores from a filesystem backup may fail with incorrect directory permissions.
-Run the [`fix-repo-permissions` command]({{< ref "cli_chef_automate/#chef-automate-backup-fix-repo-permissions" >}}) to address such issues:
+Run the [`fix-repo-permissions` command]({{< relref "cli/#chef-automate-backup-fix-repo-permissions" >}}) to address such issues:
 
 ```shell
 sudo chef-automate backup fix-repo-permissions <path>
@@ -139,7 +139,7 @@ chef-automate backup restore --airgap-bundle </path/to/bundle> gs://bucket_name/
 Use the `--patch-config` option with a [configuration patch file]({{< relref "restore.md#prerequisites" >}}) to restore to a host with a different FQDN than that of the backup host.
 
 Restores from a filesystem backup may fail with incorrect directory permissions.
-Run the [`fix-repo-permissions` command]({{< ref "cli_chef_automate/#chef-automate-backup-fix-repo-permissions" >}}) to address such issues:
+Run the [`fix-repo-permissions` command]({{< relref "cli/#chef-automate-backup-fix-repo-permissions" >}}) to address such issues:
 
 ```shell
 sudo chef-automate backup fix-repo-permissions <path>
@@ -215,4 +215,4 @@ chef-automate debug set-log-level deployment-service debug
 
 ## References
 
-See the [`chef-automate backup restore` command reference]({{< ref "cli_chef_automate/#chef-automate-backup-restore" >}}).
+See the [`chef-automate backup restore` command reference]({{< relref "cli/#chef-automate-backup-restore" >}}).
diff --git a/components/docs-chef-io/layouts/partials/cli_chef_automate_toc.html b/components/docs-chef-io/layouts/partials/cli_chef_automate_toc.html
index 843e5f9ba..d5904aaa9 100644
--- a/components/docs-chef-io/layouts/partials/cli_chef_automate_toc.html
+++ b/components/docs-chef-io/layouts/partials/cli_chef_automate_toc.html
@@ -1,53 +1,35 @@
-<aside class="sticky sidebar" data-sticky data-top-anchor="utility-bar:bottom" data-btm-anchor="footer:top">
-  <span>
-    <strong>
-      <h4 class="mini-toc-header">Table Of Contents</h4>
-    </strong>
-    <button class="close-button hide-for-large" aria-label="Close menu" type="button" data-close>
-      <span aria-hidden="true">&times;</span>
-    </button>
-  </span>
-  <p>
+<aside class="sticky sidebar hide-for-print" data-sticky data-top-anchor="utility-bar:bottom" data-btm-anchor="page-content:bottom">
+  <div id="table-of-contents">
+    <span>
+      <span class="mini-toc-nav">Table Of Contents</span>
+      <button class="close-button hide-for-large" aria-label="Close menu" type="button" data-close>
+        <span aria-hidden="true">&times;</span>
+      </button>
+    </span>
     <a href="#{{ anchorize ( .Title )}}">{{ .Title }}</a>
-    {{ $page := .Title }}
-  </p>
-  <ul>
-    <li>
-      <a href = "#automate-cli-commands">Automate CLI Commands</a>
-      <ul>
-        {{ range .Site.Data.automate.cli_chef_automate.commands }}
-        {{ if and (eq $page "Automate CLI (HA)") (or (not .compatible_with) (eq .compatible_with "AutomateHA")) }}
-          {{ $command := .name }}
-          {{ if ne $command "chef-automate"}}
-            {{ $command := replaceRE `chef-automate ` "" $command }}
-            <li>
-              <a href = "#{{ .name | urlize}}">{{ $command }}</a>
-            </li>
-          {{ else}}
-            <li>
-              <a href = "#{{ .name | urlize}}">{{ $command }}</a>
-            </li>
-          {{ end }}
-          {{ end }} 
-          {{ if and (eq $page "Automate CLI (Standalone)") (or (not .compatible_with) (eq .compatible_with "Automate")) }}
-          {{ $command := .name }}
-          {{ if ne $command "chef-automate"}}
-            {{ $command := replaceRE `chef-automate ` "" $command }}
-            <li>
-              <a href = "#{{ .name | urlize}}">{{ $command }}</a>
-            </li>
-          {{ else}}
-            <li>
-              <a href = "#{{ .name | urlize}}">{{ $command }}</a>
-            </li>
-          {{ end }}
-          {{ end }} 
+    <ul>
+      <li>
+        <a href = "#automate-cli-commands">Automate CLI Commands</a>
+        <ul>
+          {{ range .Site.Data.automate.cli_chef_automate.commands }}
+            {{ $command := .name }}
+            {{ if ne $command "chef-automate"}}
+              {{ $command := replaceRE `chef-automate ` "" $command }}
+              <li>
+                <a href = "#{{ .name | urlize}}">{{ $command }}</a>
+              </li>
+            {{ else}}
+              <li>
+                <a href = "#{{ .name | urlize}}">{{ $command }}</a>
+              </li>
+            {{ end }}
 
-        {{ end }}
-      </ul>
-    </li>
-    <li>
-      <a href = "#error-codes">Error Codes</a>
-    </li>
-  </ul>
+          {{ end }}
+        </ul>
+      </li>
+      <li>
+        <a href = "#error-codes">Error Codes</a>
+      </li>
+    </ul>
+  </div>
 </aside>
diff --git a/components/docs-chef-io/layouts/shortcodes/automate/automate_cli_commands.html b/components/docs-chef-io/layouts/shortcodes/automate/automate_cli_commands.html
index 216b914b7..6fa0dee66 100644
--- a/components/docs-chef-io/layouts/shortcodes/automate/automate_cli_commands.html
+++ b/components/docs-chef-io/layouts/shortcodes/automate/automate_cli_commands.html
@@ -2,79 +2,85 @@
 {{ range $data_path }}
 {{ $id := urlize .name }}
 {{ $command := strings.TrimPrefix "chef-automate " .name }}
-{{ if or (not .compatible_with) (eq .compatible_with "Automate") }}
-<h3 id="{{ $id }}">
-   {{ .name }}</h2>
-   <p>{{ .description }}</p>
-   <h4 id="{{ $id }}-usage">
-   Usage
-</h3>
+<h3 id="{{ $id }}">{{ .name }}</h3>
+
+<p>{{ .description | markdownify }}</p>
+
+<h4 id="{{ $id }}-supported-on">Supported on</h4>
+
+{{ if (eq .compatible_with "AutomateHA") }}
+  <p>Command available on Chef Automate high availability deployments.</p>
+{{ else if (eq .compatible_with "Automate") }}
+  <p>Command available on standalone Chef Automate deployments.</p>
+{{ else }}
+  <p>Command available on standalone and high availability Chef Automate deployments.</p>
+{{ end }}
+
+{{ if (eq .supported_on "Bastion" )}}
+  <p>In high availability Chef Automate deployments, this command is supported on the bastion host.</p>
+{{ else if (eq .supported_on "FrontEnd" ) }}
+  <p>In high availability Chef Automate deployments, this command is only supported on Chef Automate and Chef Infra Server frontend nodes.</p>
+{{ else if (eq .supported_on "Automate" )}}
+  <p>In high availability Chef Automate deployments, this command is only supported on Chef Automate nodes.</p>
+{{ end }}
+
+<h4 id="{{ $id }}-usage">Usage</h4>
+
 <pre><code>{{ .usage }}</code></pre>
-<h4 id="{{ $id }}-flags">
-Flags</h3>
-<div class="fake-code grid-x">
-   <table>
-      <tr class="table-heading">
-         <th>Flags</th>
-         <th>Description</th>
-      </tr>
-      {{- range .options -}}
-      <tr>
-         {{ if or (not .compatible_with_options) (eq .compatible_with_options "Automate") }}
-         <td>
-            <div class="cell medium-4">{{ if .shorthand }}-{{ .shorthand }}, {{ end }}--{{ .name }}</div>
-         </td>
-         <td>
-            <div class="cell medium-8">{{ .usage }}{{ if .default_value }} (default: {{ .default_value }}){{ end }}</div>
-         </td>
-         {{ end }}
-      </tr>
-      {{- end -}}
-      {{- range .inherited_options -}}
-      <tr>
-         {{ if or (not .compatible_with_options) (eq .compatible_with_options "Automate") }}
-         <td>
-            <div class="cell medium-4">{{ if .shorthand }}-{{ .shorthand }}, {{ end }}--{{ .name }}</div>
-         </td>
-         <td>
-            <div class="cell medium-8">{{ .usage }}{{ if .default_value }} (default: {{ .default_value }}){{ end }}</div>
-         </td>
-         {{ end }}
-      </tr>
-      {{- end -}}
-   </table>
-</div>
-<h4 id="{{ $id }}-see-also">
-Related Commands</h3>
-<table>
-   <div class="grid-x">
-      <tr class="table-heading">
-         <th>Commands</th>
-         <th>Description</th>
-      </tr>
-      {{ range .see_also }}
+
+<h4 id="{{ $id }}-flags">Flags</h4>
+
+<dl>
+  {{ $flags := slice }}
+  {{ range .options }}
+    {{ $flags = $flags | append . }}
+  {{ end }}
+  {{ range .inherited_options }}
+    {{ $flags = $flags | append . }}
+  {{ end }}
+
+  {{ range sort $flags "name" }}
+    {{ if .shorthand }}<dt><code>-{{ .shorthand }}</code></dt>{{ end }}
+    <dt><code>--{{ .name }}</code></dt>
+    <dd>{{.usage}}</dd>
+    {{ if (eq .compatible_with_options "Automate") }}
+      <dd>Flag only available on standalone Chef Automate deployments.</dd>
+    {{ else if (eq .compatible_with_options "AutomateHA") }}
+      <dd>Flag only available on high availability Chef Automate deployments.</dd>
+    {{ end }}
+    <dd></dd>
+    {{ with .default_value }}<dd>Default value: <code>{{ . }}</code></dd>{{ end }}
+  {{ end }}
+
+</dl>
+
+{{ if ne .name "chef-automate" }}
+  <h4 id="{{ $id }}-see-also">Related Commands</h4>
+  <ul>
+    {{ range .see_also }}
       {{ $sa_bits := split . " - " }}
       {{ $sa_id := index $sa_bits 0 }}
-      {{ $sa_string := index $sa_bits 1 }}
+
       {{ $sa_command := "" }}
       {{ $sa_command_id := "" }}
+
       {{ if ( hasPrefix $id (urlize $sa_id ) )}}
-      {{ $sa_command_id = urlize $sa_id }}
-      {{ $sa_command = strings.TrimPrefix "chef-automate " $sa_id }}
+
+        {{ $sa_command_id = urlize $sa_id }}
+        {{ $sa_command = strings.TrimPrefix "chef-automate " $sa_id }}
+
       {{ else }}
-      {{ $sa_command_id = urlize (print $id "-" $sa_id) }}
-      {{ $sa_command = print $command " " $sa_id }}
+
+        {{ $sa_command_id = urlize (print $id "-" $sa_id) }}
+        {{ $sa_command = print $command " " $sa_id }}
+
       {{ end }}
-      <tr>
-         <td>
-            <div class="cell medium-3"><a href="#{{ $sa_command_id }}">{{ $sa_command }}</a></div>
-         </td>
-         <td>
-            <div class="cell medium-9">{{ $sa_string }}</div>
-         </td>
-      </tr>
+
+      {{ if ne $sa_id "chef-automate" }}
+        <li><a href="#{{ $sa_command_id }}"><code>chef-automate {{ $sa_command }}</code></a></li>
       {{ end }}
-   </div>
-</table>
+    {{ end }}
+  </ul>
+
 {{ end }}
 {{ end }}
diff --git a/components/docs-chef-io/layouts/shortcodes/automate/automate_cli_commands_ha.html b/components/docs-chef-io/layouts/shortcodes/automate/automate_cli_commands_ha.html
deleted file mode 100644
index 990ce43c8..000000000
--- a/components/docs-chef-io/layouts/shortcodes/automate/automate_cli_commands_ha.html
+++ /dev/null
@@ -1,82 +0,0 @@
-{{ $data_path := index $.Site.Data (split (.Get "data_path") "/") }}
-{{ range $data_path }}
-{{ $id := urlize .name }}
-{{ $command := strings.TrimPrefix "chef-automate " .name }}
-{{ if or (not .compatible_with) (eq .compatible_with "AutomateHA") }}
-<h3 id="{{ $id }}">
-   {{ .name }}</h2>
-   <p>{{ .description }}</p>
-   <h4 id="{{ $id }}-usage">
-   Usage
-</h3>
-<pre><code>{{ .usage }}</code></pre>
-<h4 id="{{ $id }}-flags">
-Flags</h3>
-<div class="fake-code grid-x">
-   <table>
-      <tr class="table-heading">
-         <th>Flags</th>
-         <th>Description</th>
-      </tr>
-      {{- range .options -}}
-      <tr>
-         {{ if or (not .compatible_with_options) (eq .compatible_with_options "AutomateHA") }}
-         <td>
-            <div class="cell medium-4">{{ if .shorthand }}-{{ .shorthand }}, {{ end }}--{{ .name }}</div>
-         </td>
-         <td>
-            <div class="cell medium-8">{{ .usage }}{{ if .default_value }} (default: {{ .default_value }}){{ end }}</div>
-         </td>
-         {{ end }}
-      </tr>
-      {{- end -}}    
-      {{- range .inherited_options -}}
-      <tr>
-         {{ if or (not .compatible_with_options) (eq .compatible_with_options "AutomateHA") }}
-         <td>
-            <div class="cell medium-4">{{ if .shorthand }}-{{ .shorthand }}, {{ end }}--{{ .name }}</div>
-         </td>
-         <td>
-            <div class="cell medium-8">{{ .usage }}{{ if .default_value }} (default: {{ .default_value }}){{ end }}</div>
-         </td>
-         {{ end }}
-      </tr>
-      {{- end -}}
-   </table>
-</div>
-<h4>Supported On</h4>
-<p>{{ .supported_on }}</p>
-<h4 id="{{ $id }}-see-also">
-Related Commands</h3>   
-<table>
-   <div class="grid-x">
-      <tr class="table-heading">
-         <th>Commands</th>
-         <th>Description</th>
-      </tr>
-      {{ range .see_also }}
-      {{ $sa_bits := split . " - " }}
-      {{ $sa_id := index $sa_bits 0 }}
-      {{ $sa_string := index $sa_bits 1 }}
-      {{ $sa_command := "" }}
-      {{ $sa_command_id := "" }}
-      {{ if ( hasPrefix $id (urlize $sa_id ) )}}
-      {{ $sa_command_id = urlize $sa_id }}
-      {{ $sa_command = strings.TrimPrefix "chef-automate " $sa_id }}
-      {{ else }}
-      {{ $sa_command_id = urlize (print $id "-" $sa_id) }}
-      {{ $sa_command = print $command " " $sa_id }}
-      {{ end }}
-      <tr>
-         <td>
-            <div class="cell medium-3"><a href="#{{ $sa_command_id }}">{{ $sa_command }}</a></div>
-         </td>
-         <td>
-            <div class="cell medium-9">{{ $sa_string }}</div>
-         </td>
-      </tr>
-      {{ end }}
-   </div>
-</table>
-{{ end }}
-{{ end }}
diff --git a/dev/populate_chef_backend_data.sh b/dev/populate_chef_backend_data.sh
index 64c973390..a5c84744e 100644
--- a/dev/populate_chef_backend_data.sh
+++ b/dev/populate_chef_backend_data.sh
@@ -58,7 +58,7 @@ do
   for file in *; 
   do
     # Extract the contents of the tar file
-    knife upload file -s $SERVER_URL/organizations/"org$i" --config-option cookbook_path=/home/ubuntu/cookbook
+    knife upload $file -s $SERVER_URL/organizations/"org$i" --config-option cookbook_path=/home/ubuntu/cookbook
   done
 done
 
diff --git a/lib/config/hadeployconfigverify.go b/lib/config/hadeployconfigverify.go
index 97a50aa5e..c46f0f67c 100644
--- a/lib/config/hadeployconfigverify.go
+++ b/lib/config/hadeployconfigverify.go
@@ -595,11 +595,7 @@ func validateAwsManagedServices(aws *ConfigAwsSettings) error {
 
 func validateCommonAwsSettings(aws *ConfigAwsSettings) error {
 	errorList := list.New()
-
-	if err := validateRequiredString(aws.Profile, "aws profile name"); err != nil {
-		errorList.PushBack(err)
-	}
-
+	
 	if err := validateRequiredString(aws.Region, "aws region"); err != nil {
 		errorList.PushBack(err)
 	}
diff --git a/lib/httputils/http.go b/lib/httputils/http.go
index 54d3fc826..e40093348 100644
--- a/lib/httputils/http.go
+++ b/lib/httputils/http.go
@@ -14,6 +14,7 @@ import (
 // HTTPClient interface represents an HTTP client.
 type HTTPClient interface {
 	MakeRequest(requestMethod, url string, body interface{}) (*http.Response, []byte, error)
+	MakeRequestWithHeaders(requestMethod, url string, body interface{}, headerkey string, headerValue string) (*http.Response, []byte, error)
 }
 
 // Client is a wrapper around http.Client that adds some convenience methods for making requests and handling responses.
@@ -24,13 +25,18 @@ type Client struct {
 
 // MockHTTPClient is a mock implementation of HTTPClient.
 type MockHTTPClient struct {
-	MakeRequestFunc func(requestMethod, url string, body interface{}) (*http.Response, []byte, error)
+	MakeRequestFunc            func(requestMethod, url string, body interface{}) (*http.Response, []byte, error)
+	MakeRequestWithHeadersfunc func(requestMethod, url string, body interface{}, headerkey string, headerValue string) (*http.Response, []byte, error)
 }
 
 func (m *MockHTTPClient) MakeRequest(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
 	return m.MakeRequestFunc(requestMethod, url, body)
 }
 
+func (m *MockHTTPClient) MakeRequestWithHeaders(requestMethod, url string, body interface{}, headerkey string, headerValue string) (*http.Response, []byte, error) {
+	return m.MakeRequestWithHeadersfunc(requestMethod, url, body, headerkey, headerValue)
+}
+
 // NewClient returns a new Client with sane defaults.
 func NewClient(logger logger.Logger) *Client {
 	return &Client{
@@ -85,3 +91,39 @@ func (c *Client) MakeRequest(requestMethod, url string, body interface{}) (*http
 	c.Logger.Debugf("Response body for MakeRequest: \n%s\n", string(responseBody))
 	return resp, responseBody, nil
 }
+
+func (c *Client) MakeRequestWithHeaders(requestMethod, url string, body interface{}, headerKey string, headerValue string) (*http.Response, []byte, error) {
+	var reader io.Reader
+
+	// Marshal the request body to JSON if it's not nil
+	if body != nil {
+		requestBody, err := json.Marshal(body)
+		if err != nil {
+			return nil, nil, fmt.Errorf("failed to marshal request body: %w", err)
+		}
+		c.Logger.Debugf("Request body for MakeRequest: \n%s\n", string(requestBody))
+		reader = bytes.NewReader(requestBody)
+	}
+
+	req, err := http.NewRequest(requestMethod, url, reader)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to create HTTP request: %w", err)
+	}
+
+	req.Header.Set(headerKey, headerValue)
+
+	resp, err := c.Client.Do(req)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to make HTTP request: %w", err)
+	}
+
+	defer resp.Body.Close()
+
+	responseBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	c.Logger.Debugf("Response body for MakeRequest: \n%s\n", string(responseBody))
+	return resp, responseBody, nil
+}
diff --git a/lib/sshutils/sshutils.go b/lib/sshutils/sshutils.go
index d975ed336..f3289addc 100644
--- a/lib/sshutils/sshutils.go
+++ b/lib/sshutils/sshutils.go
@@ -1,20 +1,28 @@
 package sshutils
 
 import (
+	"fmt"
 	"net"
 	"os"
 	"path/filepath"
 	"regexp"
 	"strings"
+	"sync"
 	"time"
 
+	"github.com/chef/automate/lib/io/fileutils"
 	"github.com/chef/automate/lib/logger"
 	"github.com/chef/automate/lib/platform/command"
+	"github.com/chef/automate/lib/stringutils"
 	"github.com/pkg/errors"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh/knownhosts"
 )
 
+const AUTOMATE_KNOWN_HOSTS = ".automate_known_hosts"
+
+var mutex sync.Mutex
+
 type SSHConfig struct {
 	SshUser    string
 	SshPort    string
@@ -24,9 +32,10 @@ type SSHConfig struct {
 }
 
 type SSHUtilImpl struct {
-	SshClient ISshClient
-	logger    logger.Logger
-	Exec      command.Executor
+	SshClient  ISshClient
+	logger     logger.Logger
+	Exec       command.Executor
+	sshDirPath string
 }
 
 type SSHUtil interface {
@@ -108,6 +117,21 @@ func (s *SSHUtilImpl) GetClientConfig(sshConfig SSHConfig) (*ssh.ClientConfig, e
 		s.logger.Errorf("Parsing key failed: %v", err)
 		return nil, err
 	}
+	homePath := os.Getenv("HOME")
+	if homePath == "" {
+		return nil, errors.New("Environment variable HOME cannot be empty. Please set a value for HOME env")
+	}
+	sshDirPath := filepath.Join(os.Getenv("HOME"), ".ssh")
+	exists, err := fileutils.PathExists(sshDirPath)
+	if err != nil {
+		s.logger.Errorln("Error evaluating path:", err)
+		return nil, err
+	}
+	if !exists {
+		s.logger.Errorf("Path %s does not exist\n", sshDirPath)
+		return nil, errors.New(fmt.Sprintf("Path %s does not exist", sshDirPath))
+	}
+	s.sshDirPath = sshDirPath
 	// Client config
 	return &ssh.ClientConfig{
 		User:            sshConfig.SshUser,
@@ -117,8 +141,11 @@ func (s *SSHUtilImpl) GetClientConfig(sshConfig SSHConfig) (*ssh.ClientConfig, e
 }
 
 func (s *SSHUtilImpl) HostKeyCallback(host string, remote net.Addr, pubkey ssh.PublicKey) error {
+	mutex.Lock()
+	defer mutex.Unlock()
+	knownHostPath := filepath.Join(s.sshDirPath, AUTOMATE_KNOWN_HOSTS)
 	var keyErr *knownhosts.KeyError
-	kh, err := s.CheckKnownHosts()
+	kh, err := s.CheckKnownHosts(knownHostPath)
 	if err != nil {
 		s.logger.Error("Error while getting the list of knows Host:", err)
 		return err
@@ -134,7 +161,7 @@ func (s *SSHUtilImpl) HostKeyCallback(host string, remote net.Addr, pubkey ssh.P
 		return keyErr
 	} else if errors.As(hErr, &keyErr) && len(keyErr.Want) == 0 {
 		// host key not found in known_hosts then give a warning and continue to connect.
-		return s.AddHostKey(host, remote, pubkey)
+		return s.AddHostKey(host, knownHostPath, remote, pubkey)
 	}
 	return nil
 }
@@ -296,8 +323,8 @@ func (s *SSHUtilImpl) CopyFileToRemoteConcurrently(sshConfig SSHConfig, srcFileP
 	return results
 }
 
-func (s *SSHUtilImpl) CreateKnownHosts() error {
-	f, fErr := os.OpenFile(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"), os.O_CREATE, 0600)
+func (s *SSHUtilImpl) CreateKnownHosts(knownHostPath string) error {
+	f, fErr := os.OpenFile(knownHostPath, os.O_CREATE, 0600)
 	if fErr != nil {
 		return fErr
 	}
@@ -305,12 +332,12 @@ func (s *SSHUtilImpl) CreateKnownHosts() error {
 	return nil
 }
 
-func (s *SSHUtilImpl) CheckKnownHosts() (ssh.HostKeyCallback, error) {
-	err := s.CreateKnownHosts()
+func (s *SSHUtilImpl) CheckKnownHosts(knownHostPath string) (ssh.HostKeyCallback, error) {
+	err := s.CreateKnownHosts(knownHostPath)
 	if err != nil {
 		return nil, err
 	}
-	kh, e := s.SshClient.New(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
+	kh, e := s.SshClient.New(knownHostPath)
 	if e != nil {
 		s.logger.Error("Error while getting the known host: %v", e)
 		return nil, e
@@ -318,19 +345,35 @@ func (s *SSHUtilImpl) CheckKnownHosts() (ssh.HostKeyCallback, error) {
 	return kh, nil
 }
 
-func (s *SSHUtilImpl) AddHostKey(host string, remote net.Addr, pubKey ssh.PublicKey) error {
-	// add host key if host is not found in known_hosts, error object is return, if nil then connection proceeds,
+func (s *SSHUtilImpl) AddHostKey(host, known_hosts_path string, remote net.Addr, pubKey ssh.PublicKey) error {
+	// add host key if host is not found in known_hosts, error object is returned, if nil then connection proceeds,
 	// if not nil then connection stops.
-	khFilePath := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
+	content, err := os.ReadFile(known_hosts_path)
+	if err != nil {
+		s.logger.Error("Error while reading the known hosts file:", err)
+		return err
+	}
+	lastLine := stringutils.GetLastLine(string(content))
 
-	f, fErr := os.OpenFile(khFilePath, os.O_APPEND|os.O_WRONLY, 0600)
-	if fErr != nil {
-		s.logger.Error("Error while opening file on the given path:", fErr)
-		return fErr
+	// Open the known hosts file in append mode
+	f, err := os.OpenFile(known_hosts_path, os.O_APPEND|os.O_WRONLY, 0600)
+	if err != nil {
+		s.logger.Error("Error while opening file on the given path:", err)
+		return err
 	}
 	defer f.Close()
+
+	// Write the host key entry to the known hosts file
 	knownHosts := s.SshClient.Normalize(remote.String())
-	_, fileErr := f.WriteString(knownhosts.Line([]string{knownHosts}, pubKey))
-	f.WriteString("\n")
-	return fileErr
+	line := knownhosts.Line([]string{knownHosts}, pubKey)
+	if lastLine != "" {
+		line = "\n" + line
+	}
+	_, err = f.WriteString(line)
+	if err != nil {
+		s.logger.Error("Error while writing to the known hosts file:", err)
+		return err
+	}
+
+	return nil
 }
diff --git a/lib/stringutils/strings.go b/lib/stringutils/strings.go
index 4a19ce794..07827d6a0 100644
--- a/lib/stringutils/strings.go
+++ b/lib/stringutils/strings.go
@@ -28,3 +28,9 @@ func TitleReplace(s string, sep string, rep string) string {
 func IsNumeric(word string) bool {
 	return regexp.MustCompile(`^[0-9]+$`).MatchString(word)
 }
+
+func GetLastLine(input string) string {
+	lines := strings.Split(input, "\n")
+	lastLine := lines[len(lines)-1]
+	return lastLine
+}
diff --git a/terraform/a2ha-terraform/modules/aws_metadata/main.tf b/terraform/a2ha-terraform/modules/aws_metadata/main.tf
index 411d5a67b..1a3c31053 100644
--- a/terraform/a2ha-terraform/modules/aws_metadata/main.tf
+++ b/terraform/a2ha-terraform/modules/aws_metadata/main.tf
@@ -15,3 +15,10 @@ data "http" "getEc2PrivateIP" {
     X-aws-ec2-metadata-token = chomp(data.local_file.readToken.content)
   }
 }
+
+data "http" "getBastionRole" {
+  url = "http://169.254.169.254/latest/meta-data/iam/info"
+  request_headers = {
+    X-aws-ec2-metadata-token = chomp(data.local_file.readToken.content)
+  }
+}
diff --git a/terraform/a2ha-terraform/modules/aws_metadata/output.tf b/terraform/a2ha-terraform/modules/aws_metadata/output.tf
index 4209b78bb..e606c7b8c 100644
--- a/terraform/a2ha-terraform/modules/aws_metadata/output.tf
+++ b/terraform/a2ha-terraform/modules/aws_metadata/output.tf
@@ -1,3 +1,7 @@
 output "json_data" {
   value = "${data.http.getEc2PrivateIP.response_body}/32"
 }
+
+output "bastion_role" {
+  value = "${data.http.getBastionRole.status_code}"
+}
diff --git a/terraform/a2ha-terraform/modules/efs/files/mount_nfs.tpl b/terraform/a2ha-terraform/modules/efs/files/mount_nfs.tpl
index b8c64de82..f433e5fa4 100644
--- a/terraform/a2ha-terraform/modules/efs/files/mount_nfs.tpl
+++ b/terraform/a2ha-terraform/modules/efs/files/mount_nfs.tpl
@@ -1,46 +1,49 @@
 #!/bin/bash -x
 
-if  which yum &> /dev/null; then
-  echo "yum package manager found; install redhat style packages"
-  yum install -y nfs-utils bind-utils
-elif which apt &> /dev/null; then
-  echo "apt package manager found; install debian style packages"
-  until [[ -f /var/lib/cloud/instance/boot-finished ]]; do
-    echo "sleeping until cloudinit finishes"
-    sleep 1
-  done
-  apt update
-  apt install -y nfs-common dnsutils
+if  grep -qs "${mount_path}" /proc/mounts; then
+    echo "Mountpoint exist"
 else
-  echo "No supported package manager found"
-  echo "Supported OS's: Redhat, OEL, Centos, Ubuntu"
-fi
-# end conditional
-NFS_SRC="${efs_mount_dns}"
-NFS_OPTIONS="nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2"
-
-mkdir -p ${mount_path}
-echo "$NFS_SRC:/ ${mount_path} nfs4 $NFS_OPTIONS 0 0" >> /etc/fstab
+    if  which yum &> /dev/null; then
+        echo "yum package manager found; install redhat style packages"
+        yum install -y nfs-utils bind-utils
+    elif which apt &> /dev/null; then
+        echo "apt package manager found; install debian style packages"
+        until [[ -f /var/lib/cloud/instance/boot-finished ]]; do
+        echo "sleeping until cloudinit finishes"
+        sleep 1
+    done
+        apt update
+        apt install -y nfs-common dnsutils
+    else
+        echo "No supported package manager found"
+        echo "Supported OS's: Redhat, OEL, Centos, Ubuntu"
+    fi
+    # end conditional
+    NFS_SRC="${efs_mount_dns}"
+    NFS_OPTIONS="nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2"
 
-# wait until DNS lookups succeed, they don't happen right away
-failure() {
-  echo "$1"
-  exit 1
-}
-wait_for_efs_dns() {
-  max=30
-  n=0
-  until [ $n -ge $max ]; do
-    grep '${mount_path}' /proc/mounts && break
-    host $NFS_SRC && mount ${mount_path} && break
-    n=$((n+1))
-    echo "Waiting EFS mount $NFS_SRC to appear in DNS"
-    sleep 11 # As AWS EFS DNS takes more time to come up
-  done
-  if [[ $n -ge $max ]]; then
-    failure "Failed waiting for EFS mount $NFS_SRC to appear in DNS"
-  fi
-}
-wait_for_efs_dns
+    mkdir -p ${mount_path}
+    echo "$NFS_SRC:/ ${mount_path} nfs4 $NFS_OPTIONS 0 0" >> /etc/fstab
 
-chmod 777 ${mount_path}
+    # wait until DNS lookups succeed, they don't happen right away
+    failure() {
+      echo "$1"
+      exit 1
+    }
+    wait_for_efs_dns() {
+       max=30
+       n=0
+       until [ $n -ge $max ]; do
+          grep '${mount_path}' /proc/mounts && break
+          host $NFS_SRC && mount ${mount_path} && break
+          n=$((n+1))
+          echo "Waiting EFS mount $NFS_SRC to appear in DNS"
+          sleep 11 # As AWS EFS DNS takes more time to come up
+       done
+       if [[ $n -ge $max ]]; then
+          failure "Failed waiting for EFS mount $NFS_SRC to appear in DNS"
+       fi
+    }
+    wait_for_efs_dns
+    chmod 777 ${mount_path}  
+fi
diff --git a/terraform/a2ha-terraform/modules/efs/main.tf b/terraform/a2ha-terraform/modules/efs/main.tf
index 657e942fa..d75ae44d0 100644
--- a/terraform/a2ha-terraform/modules/efs/main.tf
+++ b/terraform/a2ha-terraform/modules/efs/main.tf
@@ -5,7 +5,7 @@ resource "aws_efs_file_system" "backups" {
 }
 
 resource "aws_efs_mount_target" "backups" {
-  count           = 3
+  count           = length(var.subnet_id)
   file_system_id  = aws_efs_file_system.backups.id
   subnet_id       = element(var.subnet_id, count.index)
   security_groups = [var.mount_id]
diff --git a/terraform/a2ha-terraform/reference_architectures/aws/main.tf b/terraform/a2ha-terraform/reference_architectures/aws/main.tf
index b8ce743e1..86ab8c7a5 100644
--- a/terraform/a2ha-terraform/reference_architectures/aws/main.tf
+++ b/terraform/a2ha-terraform/reference_architectures/aws/main.tf
@@ -80,8 +80,6 @@ module "efs" {
   tag_name                = var.tag_name
   subnet_id               = module.aws.subnet_id
   mount_id                = module.aws.mount_id
-
-  depends_on = [module.aws]
 }
 
 module "s3" {
diff --git a/terraform/a2ha-terraform/reference_architectures/aws/variables.tf b/terraform/a2ha-terraform/reference_architectures/aws/variables.tf
index cacbde55f..880942e6d 100644
--- a/terraform/a2ha-terraform/reference_architectures/aws/variables.tf
+++ b/terraform/a2ha-terraform/reference_architectures/aws/variables.tf
@@ -51,7 +51,7 @@ variable "aws_os_snapshot_role_arn" {
 }
 
 variable "aws_profile" {
-  default     = "default"
+  default     = ""
   description = "The AWS profile to use from your ~/.aws/credentials file."
 }
 


Test patch:
diff --git a/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment_test.go b/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment_test.go
new file mode 100644
index 000000000..4d010f8f9
--- /dev/null
+++ b/components/automate-cli/cmd/chef-automate/automateHAAwsDeployment_test.go
@@ -0,0 +1,106 @@
+package main
+
+import (
+	"net/http"
+	"testing"
+
+	"github.com/chef/automate/lib/httputils"
+	"github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetBastionIamRole(t *testing.T) {
+	ser := newAwsDeployemnt("")
+	tests := []struct {
+		description        string
+		MockhttputilsToken httputils.HTTPClient
+		MockhttputilsIam   httputils.HTTPClient
+		wantError          error
+		testurl            string
+	}{
+		{
+			description: "Failed to get token value from the request",
+			MockhttputilsToken: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					return nil, nil, errors.New("failed to marshal request body")
+				},
+			},
+			wantError: errors.New("error while getting the token value: failed to marshal request body"),
+			testurl:   TOKEN_URL,
+		},
+		{
+			description: "Failed to get token value from the request (error in creation of request)",
+			MockhttputilsToken: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					return nil, nil, errors.New("failed to create HTTP request")
+				},
+			},
+			wantError: errors.New("error while getting the token value: failed to create HTTP request"),
+			testurl:   TOKEN_URL,
+		},
+		{
+			description: "Bastion IAM role is not attached",
+			MockhttputilsToken: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					return &http.Response{}, nil, nil
+				},
+			},
+			MockhttputilsIam: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					return &http.Response{}, []byte("Success"), nil
+				},
+			},
+			wantError: errors.New("Please check if Bastion has attached an IAM Role to it"),
+			testurl:   METADATA_URL,
+		},
+		{
+			description: "Bastion IAM role is not attached (Error)",
+			MockhttputilsToken: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					return &http.Response{}, nil, nil
+				},
+			},
+			MockhttputilsIam: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					if requestMethod == http.MethodPut {
+						return nil, nil, nil
+					}
+					return nil, nil, errors.New("failed to Make HTTP Request")
+				},
+			},
+			wantError: errors.New("error while getting the response for IAM role: failed to Make HTTP Request"),
+			testurl:   METADATA_URL,
+		},
+		{
+			description: "Bastion IAM role is attached",
+			MockhttputilsToken: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					return &http.Response{}, nil, nil
+				},
+			},
+			MockhttputilsIam: &httputils.MockHTTPClient{
+				MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+					return &http.Response{
+						StatusCode: 200,
+					}, []byte("Success"), nil
+				},
+			},
+			wantError: nil,
+			testurl:   METADATA_URL,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			if tt.testurl == TOKEN_URL {
+				ser.httpRequestClient = tt.MockhttputilsToken
+			} else if tt.testurl == METADATA_URL {
+				ser.httpRequestClient = tt.MockhttputilsIam
+			}
+			err := ser.isIamRolePresent()
+			if err != nil {
+				assert.Equal(t, err.Error(), tt.wantError.Error())
+			}
+		})
+	}
+}
diff --git a/components/automate-cli/cmd/chef-automate/certRotate_test.go b/components/automate-cli/cmd/chef-automate/certRotate_test.go
index cde597978..0dd202698 100644
--- a/components/automate-cli/cmd/chef-automate/certRotate_test.go
+++ b/components/automate-cli/cmd/chef-automate/certRotate_test.go
@@ -2,11 +2,14 @@ package main
 
 import (
 	"os"
+	"path/filepath"
 	"testing"
+	"time"
 
 	"github.com/chef/automate/api/config/deployment"
 	"github.com/chef/automate/api/config/shared"
 	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/sshutils"
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/protobuf/types/known/wrapperspb"
@@ -49,11 +52,247 @@ xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
 VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
 WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
 -----END CERTIFICATE-----`
-	ValidCertPath = "./certRotate.go"
+	ValidCertPath                = "./certRotate.go"
+	TestOpensearchAdminAndRootCA = `
+	[tls]
+	rootCA = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+	admin_cert = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+	admin_key = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+	ssl_cert = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+	ssl_key = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+[plugins.security.authcz]
+	admin_dn = '- CN=chefadmin,O=Chef Software Inc,L=Seattle,ST=Washington,C=US'
+[plugins.security.ssl.transport]
+	enforce_hostname_verification = false
+	resolve_hostname = false
+[plugins.security]
+	nodes_dn = '- CN=chefnode1,O=Chef Software Inc,L=Seattle,ST=Washington,C=US'`
+	TestFrontendConfig = `[[load_balancer.v1.sys.frontend_tls]]
+	cert = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+	key = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+[[global.v1.frontend_tls]]
+	cert = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""
+	key = """-----BEGIN CERTIFICATE-----
+	MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
+	MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
+	U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
+	NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
+	ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
+	ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
+	DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
+	8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+	+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
+	X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
+	K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
+	1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
+	A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
+	zt0fhvRbVazc1xDCDqmI56FspGowaaBCDEfGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
+	YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
+	bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
+	DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
+	L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
+	eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
+	xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
+	VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
+	WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8xY5Z=
+	-----END CERTIFICATE-----"""`
+	testfile = `./testfiles/ssh`
 )
 
+var sshConfig = sshutils.SSHConfig{
+	SshUser:    "ubuntu",
+	SshPort:    "22",
+	SshKeyFile: testfile,
+	HostIP:     ValidIP,
+	Timeout:    150,
+}
+
 func TestIsRemotePath(t *testing.T) {
-	c := certRotateFlow{FileUtils: mockFS()}
+	c := certRotateFlow{fileUtils: mockFS()}
 
 	type testCaseInfo struct {
 		testCaseDescription string
@@ -98,7 +337,7 @@ func TestIsRemotePath(t *testing.T) {
 }
 
 func TestGetIPV4(t *testing.T) {
-	c := certRotateFlow{FileUtils: mockFS()}
+	c := certRotateFlow{fileUtils: mockFS()}
 
 	type testCaseInfo struct {
 		testCaseDescription string
@@ -143,7 +382,7 @@ func TestGetIPV4(t *testing.T) {
 }
 
 func TestGetRemoteFileDetails(t *testing.T) {
-	c := certRotateFlow{FileUtils: mockFS()}
+	c := certRotateFlow{fileUtils: mockFS()}
 
 	type testCaseInfo struct {
 		testCaseDescription    string
@@ -238,7 +477,7 @@ func TestGetRemoteFileDetails(t *testing.T) {
 }
 
 func TestGetCerts(t *testing.T) {
-	c := certRotateFlow{FileUtils: mockFS()}
+	c := certRotateFlow{fileUtils: mockFS()}
 
 	infra := &AutomateHAInfraDetails{}
 
@@ -708,63 +947,6 @@ func TestCompareCurrentCertsWithNewCerts(t *testing.T) {
 	}
 }
 
-func TestGetFrontEndIpsForSkippingRootCAPatching(t *testing.T) {
-	c, infra := getMockCertRotateFlowAndInfra()
-	type testCaseInfo struct {
-		testCaseDescription string
-		remoteService       string
-		newRootCA           string
-		currentCertsInfo    *certShowCertificates
-		skipIpsList         []string
-	}
-
-	testCases := []testCaseInfo{
-		{
-			testCaseDescription: "Automate root-ca patching in chef-server | same root-ca",
-			remoteService:       AUTOMATE,
-			newRootCA:           FileContent,
-			currentCertsInfo: &certShowCertificates{
-				AutomateRootCert: FileContent,
-			},
-			skipIpsList: c.getIps(CHEF_SERVER, infra),
-		},
-		{
-			testCaseDescription: "Automate root-ca patching in chef-server | different root-ca",
-			remoteService:       AUTOMATE,
-			newRootCA:           FileContent + "a",
-			currentCertsInfo: &certShowCertificates{
-				AutomateRootCert: FileContent,
-			},
-			skipIpsList: []string{},
-		},
-		{
-			testCaseDescription: "Postgresql root-ca patching in frontend | same root-ca",
-			remoteService:       POSTGRESQL,
-			newRootCA:           FileContent,
-			currentCertsInfo: &certShowCertificates{
-				PostgresqlRootCert: FileContent,
-			},
-			skipIpsList: c.getIps("frontend", infra),
-		},
-		{
-			testCaseDescription: "Postgresql root-ca patching in frontEnd | different root-ca",
-			remoteService:       POSTGRESQL,
-			newRootCA:           FileContent + "a",
-			currentCertsInfo: &certShowCertificates{
-				PostgresqlRootCert: FileContent,
-			},
-			skipIpsList: []string{},
-		},
-	}
-
-	for _, testCase := range testCases {
-		t.Run(testCase.testCaseDescription, func(t *testing.T) {
-			skipIpsListGot := c.getFrontEndIpsForSkippingRootCAPatching(testCase.remoteService, testCase.newRootCA, infra, testCase.currentCertsInfo)
-			assert.Equal(t, testCase.skipIpsList, skipIpsListGot)
-		})
-	}
-}
-
 func TestGetFrontEndIpsForSkippingCnAndRootCaPatching(t *testing.T) {
 	c, infra := getMockCertRotateFlowAndInfra()
 	nodesDn, _ := getDistinguishedNameFromKey(FileContent)
@@ -775,70 +957,71 @@ func TestGetFrontEndIpsForSkippingCnAndRootCaPatching(t *testing.T) {
 		newRootCA           string
 		newCn               string
 		oldCn               string
+		oldRootCA           string
 		node                string
-		currentCertsInfo    *certShowCertificates
 		infra               *AutomateHAInfraDetails
-		skipIpsList         []string
+		isIpSkip            bool
 	}
 
 	testCases := []testCaseInfo{
 		{
-			testCaseDescription: "Opensearch root-ca and cn patching | same cn and root-ca",
+			testCaseDescription: "Comparing old cn nd old rootCA | same cn and root-ca",
 			newRootCA:           FileContent,
+			oldRootCA:           FileContent,
 			newCn:               newCn,
 			oldCn:               newCn,
-			currentCertsInfo: &certShowCertificates{
-				OpensearchRootCert: FileContent,
-			},
-			infra:       infra,
-			skipIpsList: c.getIps("frontend", infra),
+			infra:               infra,
+			isIpSkip:            true,
 		},
 		{
-			testCaseDescription: "Opensearch root-ca and cn patching | same root-ca different cn",
+			testCaseDescription: "Comparing old cn nd old rootCA | same root-ca different cn",
 			newRootCA:           FileContent,
+			oldRootCA:           FileContent,
 			newCn:               newCn + "n",
 			oldCn:               "",
-			currentCertsInfo: &certShowCertificates{
-				OpensearchRootCert: FileContent,
-			},
-			infra:       infra,
-			skipIpsList: []string{},
+			infra:               infra,
+			isIpSkip:            false,
 		},
 		{
-			testCaseDescription: "Opensearch root-ca and cn patching | same cn and node flag",
+			testCaseDescription: "Comparing old cn nd old rootCA | different root-ca",
+			newRootCA:           FileContent + "a",
+			oldRootCA:           FileContent,
 			newCn:               newCn,
 			oldCn:               newCn,
-			node:                ValidIP4,
-			currentCertsInfo:    &certShowCertificates{},
 			infra:               infra,
-			skipIpsList:         c.getIps("frontend", infra),
+			isIpSkip:            false,
 		},
 		{
-			testCaseDescription: "Opensearch root-ca and cn patching | different cn and node flag",
-			newCn:               newCn + "n",
+			testCaseDescription: "Comparing old cn nd old rootCA | different root-ca same cn",
+			newRootCA:           FileContent + "a",
+			oldRootCA:           FileContent,
+			newCn:               newCn,
 			oldCn:               newCn,
-			node:                ValidIP4,
-			currentCertsInfo:    &certShowCertificates{},
 			infra:               infra,
-			skipIpsList:         []string{},
+			isIpSkip:            false,
 		},
 		{
-			testCaseDescription: "Opensearch root-ca and cn patching | different root-ca",
-			newRootCA:           FileContent + "a",
+			testCaseDescription: "Comparing old cn | different cn with node flag",
+			newCn:               newCn + "a",
+			oldCn:               newCn,
+			infra:               infra,
+			node:                ValidIP,
+			isIpSkip:            false,
+		},
+		{
+			testCaseDescription: "Comparing old cn | same cn with node flag",
 			newCn:               newCn,
 			oldCn:               newCn,
-			currentCertsInfo: &certShowCertificates{
-				OpensearchRootCert: FileContent,
-			},
-			infra:       infra,
-			skipIpsList: []string{},
+			infra:               infra,
+			node:                ValidIP,
+			isIpSkip:            true,
 		},
 	}
 
 	for _, testCase := range testCases {
 		t.Run(testCase.testCaseDescription, func(t *testing.T) {
-			skipIpsListGot := c.getFrontEndIpsForSkippingCnAndRootCaPatching(testCase.newRootCA, testCase.newCn, testCase.oldCn, testCase.node, testCase.currentCertsInfo, infra)
-			assert.Equal(t, testCase.skipIpsList, skipIpsListGot)
+			isIpSkipped := c.getFrontEndIpsForSkippingCnAndRootCaPatching(testCase.newRootCA, testCase.newCn, testCase.oldCn, testCase.oldRootCA, testCase.node)
+			assert.Equal(t, testCase.isIpSkip, isIpSkipped)
 		})
 	}
 }
@@ -878,16 +1061,23 @@ func TestGetFilteredIps(t *testing.T) {
 	}
 }
 
-func TestGetOldCn(t *testing.T) {
+func TestGetFrontIpsToSkipRootCAandCNPatchingForOs(t *testing.T) {
+	c, infra := getMockCertRotateFlowAndInfra()
 	type testCaseInfo struct {
 		testCaseDescription string
 		automatesConfig     *deployment.AutomateConfig
-		OldCnExpected       string
+		newRootCA           string
+		oldRootCA           string
+		newCn               string
+		oldCn               string
+		node                string
+		infra               *AutomateHAInfraDetails
+		skipIpsList         []string
 	}
 
 	testCases := []testCaseInfo{
 		{
-			testCaseDescription: "Getting cn value",
+			testCaseDescription: "Opensearch root-ca and cn patching | same rootca and CN ",
 			automatesConfig: &deployment.AutomateConfig{
 				Global: &shared.GlobalConfig{
 					V1: &shared.V1{
@@ -897,72 +1087,1245 @@ func TestGetOldCn(t *testing.T) {
 									ServerName: &wrapperspb.StringValue{
 										Value: "chefnode",
 									},
+									RootCert: &wrapperspb.StringValue{
+										Value: FileContent,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			oldRootCA:   FileContent,
+			newRootCA:   FileContent,
+			newCn:       "chefnode",
+			oldCn:       "chefnode",
+			infra:       infra,
+			skipIpsList: []string{ValidIP},
+		},
+		{
+			testCaseDescription: "Opensearch root-ca and cn patching | diff rootca and CN",
+			automatesConfig: &deployment.AutomateConfig{
+				Global: &shared.GlobalConfig{
+					V1: &shared.V1{
+						External: &shared.External{Opensearch: &shared.External_Opensearch{
+							Ssl: &shared.External_Opensearch_SSL{
+								ServerName: &wrapperspb.StringValue{
+									Value: "chefnode",
+								},
+								RootCert: &wrapperspb.StringValue{
+									Value: FileContent,
+								},
+							},
+						},
+						},
+					},
+				},
+			},
+			oldRootCA:   FileContent,
+			newRootCA:   FileContent + "a",
+			newCn:       "chefnode1",
+			oldCn:       "chefnode",
+			infra:       infra,
+			skipIpsList: []string{},
+		},
+		{
+			testCaseDescription: "Opensearch root-ca and cn patching | same rootca and diff CN",
+			automatesConfig: &deployment.AutomateConfig{
+				Global: &shared.GlobalConfig{
+					V1: &shared.V1{
+						External: &shared.External{Opensearch: &shared.External_Opensearch{
+							Ssl: &shared.External_Opensearch_SSL{
+								ServerName: &wrapperspb.StringValue{
+									Value: "chefnode",
+								},
+								RootCert: &wrapperspb.StringValue{
+									Value: FileContent,
+								},
+							},
+						},
+						},
+					},
+				},
+			},
+			oldRootCA:   FileContent,
+			newRootCA:   FileContent,
+			newCn:       "chefnode1",
+			oldCn:       "chefnode",
+			infra:       infra,
+			skipIpsList: []string{},
+		},
+		{
+			testCaseDescription: "Opensearch root-ca and cn patching | diff rootca and same CN",
+			automatesConfig: &deployment.AutomateConfig{
+				Global: &shared.GlobalConfig{
+					V1: &shared.V1{
+						External: &shared.External{Opensearch: &shared.External_Opensearch{
+							Ssl: &shared.External_Opensearch_SSL{
+								ServerName: &wrapperspb.StringValue{
+									Value: "chefnode",
+								},
+								RootCert: &wrapperspb.StringValue{
+									Value: FileContent,
 								},
 							},
 						},
+						},
 					},
 				},
 			},
-			OldCnExpected: "chefnode",
+			oldRootCA:   FileContent,
+			newRootCA:   FileContent + "a",
+			newCn:       "chefnode",
+			oldCn:       "chefnode",
+			infra:       infra,
+			skipIpsList: []string{},
 		},
 		{
-			testCaseDescription: "Getting cn value with field are nil",
+			testCaseDescription: "Opensearch root-ca and cn patching | diff CN with node flag",
 			automatesConfig: &deployment.AutomateConfig{
 				Global: &shared.GlobalConfig{
 					V1: &shared.V1{
-						External: &shared.External{},
+						External: &shared.External{Opensearch: &shared.External_Opensearch{
+							Ssl: &shared.External_Opensearch_SSL{
+								ServerName: &wrapperspb.StringValue{
+									Value: "chefnode",
+								},
+							},
+						},
+						},
 					},
 				},
 			},
-			OldCnExpected: "",
+			newCn:       "chefnode1",
+			oldCn:       "chefnode",
+			node:        ValidIP,
+			infra:       infra,
+			skipIpsList: []string{},
 		},
 		{
-			testCaseDescription: "Getting cn value server name as nil",
+			testCaseDescription: "Opensearch root-ca and cn patching | same CN with node flag",
+			automatesConfig: &deployment.AutomateConfig{
+				Global: &shared.GlobalConfig{
+					V1: &shared.V1{
+						External: &shared.External{Opensearch: &shared.External_Opensearch{
+							Ssl: &shared.External_Opensearch_SSL{
+								ServerName: &wrapperspb.StringValue{
+									Value: "chefnode",
+								},
+							},
+						},
+						},
+					},
+				},
+			},
+			newCn:       "chefnode",
+			oldCn:       "chefnode",
+			node:        ValidIP,
+			infra:       infra,
+			skipIpsList: []string{ValidIP},
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.testCaseDescription, func(t *testing.T) {
+			configMap := map[string]*deployment.AutomateConfig{
+				ValidIP: testCase.automatesConfig,
+			}
+			skipIpsListGot := c.getFrontIpsToSkipRootCAandCNPatchingForOs(configMap, testCase.newRootCA, testCase.newCn, testCase.node, infra)
+			assert.Equal(t, testCase.skipIpsList, skipIpsListGot)
+
+		})
+	}
+}
+
+func TestGetFrontendIPsToSkipRootCAPatchingForPg(t *testing.T) {
+	c, infra := getMockCertRotateFlowAndInfra()
+	type testCaseInfo struct {
+		testCaseDescription string
+		automatesConfig     *deployment.AutomateConfig
+		newRootCA           string
+		oldRootCA           string
+		infra               *AutomateHAInfraDetails
+		skipIpsList         []string
+	}
+
+	testCases := []testCaseInfo{
+		{
+			testCaseDescription: "Postgresql root-ca comparing | same rootca ",
 			automatesConfig: &deployment.AutomateConfig{
 				Global: &shared.GlobalConfig{
 					V1: &shared.V1{
 						External: &shared.External{
-							Opensearch: &shared.External_Opensearch{
-								Ssl: &shared.External_Opensearch_SSL{},
+							Postgresql: &shared.External_Postgresql{
+								Ssl: &shared.External_Postgresql_SSL{
+									RootCert: &wrapperspb.StringValue{
+										Value: FileContent,
+									},
+								},
 							},
 						},
 					},
 				},
 			},
-			OldCnExpected: "",
+			oldRootCA:   FileContent,
+			newRootCA:   FileContent,
+			infra:       infra,
+			skipIpsList: []string{ValidIP},
 		},
 		{
-			testCaseDescription: "Getting cn value server name as nil",
+			testCaseDescription: "Postgresql root-ca comparing | diff rootca ",
 			automatesConfig: &deployment.AutomateConfig{
 				Global: &shared.GlobalConfig{
 					V1: &shared.V1{
-						External: &shared.External{},
+						External: &shared.External{
+							Postgresql: &shared.External_Postgresql{
+								Ssl: &shared.External_Postgresql_SSL{
+									RootCert: &wrapperspb.StringValue{
+										Value: FileContent,
+									},
+								},
+							},
+						},
 					},
 				},
 			},
-			OldCnExpected: "",
+			oldRootCA:   FileContent,
+			newRootCA:   FileContent + "a",
+			infra:       infra,
+			skipIpsList: []string{},
 		},
 	}
 
 	for _, testCase := range testCases {
-		t.Run("GetOldCn", func(t *testing.T) {
+		t.Run(testCase.testCaseDescription, func(t *testing.T) {
 			configMap := map[string]*deployment.AutomateConfig{
 				ValidIP: testCase.automatesConfig,
 			}
-			oldCn := getOldCn(configMap)
-			assert.Equal(t, testCase.OldCnExpected, oldCn)
+			skipIpsListGot := c.getFrontendIPsToSkipRootCAPatchingForPg(configMap, testCase.newRootCA, infra)
+			assert.Equal(t, testCase.skipIpsList, skipIpsListGot)
+
 		})
 	}
 }
 
-func getMockCertRotateFlowAndInfra() (certRotateFlow, *AutomateHAInfraDetails) {
-	c := certRotateFlow{FileUtils: mockFS()}
+func TestGetSkipIpsListForPgRootCAPatching(t *testing.T) {
+	infra := NewMockInfra()
+	c := NewCertRotate()
+
+	type testCaseInfo struct {
+		description     string
+		sshUtil         SSHUtil
+		certs           *certificates
+		infra           *AutomateHAInfraDetails
+		MockPullConfigs PullConfigs
+		skipIpsList     []string
+		ExpectedError   string
+		wantError       bool
+	}
+	testCases := []testCaseInfo{
+		{
+			description: "same root ca",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				publicCert:  FileContent,
+				privateCert: FileContent,
+				rootCA:      FileContent,
+			},
+			infra: infra,
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP7: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Postgresql: &shared.External_Postgresql{
+											Ssl: &shared.External_Postgresql_SSL{
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP7: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Postgresql: &shared.External_Postgresql{
+											Ssl: &shared.External_Postgresql_SSL{
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{ValidIP7, ValidIP7},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "Different rootca",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent + "a",
+			},
+			infra: infra,
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP7: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Postgresql: &shared.External_Postgresql{
+											Ssl: &shared.External_Postgresql_SSL{
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP7: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Postgresql: &shared.External_Postgresql{
+											Ssl: &shared.External_Postgresql_SSL{
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "Error while pulling chefserver config",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				publicCert:  FileContent,
+				privateCert: FileContent,
+				rootCA:      FileContent,
+			},
+			infra: infra,
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP7: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Postgresql: &shared.External_Postgresql{
+											Ssl: &shared.External_Postgresql_SSL{
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return nil, errors.New("ERROR")
+				},
+			},
+
+			skipIpsList:   []string{},
+			ExpectedError: "ERROR",
+			wantError:     true,
+		},
+		{
+			description: "Error while pulling Automate config",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				publicCert:  FileContent,
+				privateCert: FileContent,
+				rootCA:      FileContent,
+			},
+			infra: infra,
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return nil, errors.New("ERROR")
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return nil, errors.New("ERROR")
+				},
+			},
+
+			skipIpsList:   []string{},
+			ExpectedError: "ERROR",
+			wantError:     true,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			c.pullConfigs = testCase.MockPullConfigs
+			skipIpsListGot, err := c.getSkipIpsListForPgRootCAPatching(testCase.infra, testCase.sshUtil, testCase.certs)
+			if !testCase.wantError {
+				assert.Equal(t, testCase.skipIpsList, skipIpsListGot)
+			} else {
+				assert.Error(t, err)
+			}
+
+		})
+	}
+}
+
+func TestGetSkipIpsListForOsRootCACNPatching(t *testing.T) {
+	infra := NewMockInfra()
+	c := NewCertRotate()
+
+	type testCaseInfo struct {
+		description     string
+		sshUtil         SSHUtil
+		certs           *certificates
+		infra           *AutomateHAInfraDetails
+		MockPullConfigs PullConfigs
+		nodesCn         string
+		flagsObj        *certRotateFlags
+		skipIpsList     []string
+		ExpectedError   string
+		wantError       bool
+	}
+	testCases := []testCaseInfo{
+		{
+			description: "same root ca and cn in all ips",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent,
+			},
+			infra:    infra,
+			nodesCn:  "chefnode",
+			flagsObj: &certRotateFlags{},
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP1: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{ValidIP, ValidIP1},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "same root ca in all ips but different cn in one ip",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent,
+			},
+			infra:    infra,
+			nodesCn:  "chefnode",
+			flagsObj: &certRotateFlags{},
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP1: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnodee",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{ValidIP},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "diff root ca and cn in all ips",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent + "a",
+			},
+			infra:    infra,
+			nodesCn:  "chefnodee",
+			flagsObj: &certRotateFlags{},
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP1: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "same CN with node flag",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent,
+			},
+			infra: infra,
+			flagsObj: &certRotateFlags{
+				node: ValidIP4,
+			},
+			nodesCn: "chefnode",
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP1: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{ValidIP, ValidIP1},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "diff CN with node flag",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent + "a",
+			},
+			infra: infra,
+			flagsObj: &certRotateFlags{
+				node: ValidIP4,
+			},
+			nodesCn: "chefnode",
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnodee",
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP1: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnodee",
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "Error while fetching chefserver config",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				publicCert:  FileContent,
+				privateCert: FileContent,
+				rootCA:      FileContent,
+			},
+			infra: infra,
+			flagsObj: &certRotateFlags{
+				node: ValidIP4,
+			},
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP7: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Postgresql: &shared.External_Postgresql{
+											Ssl: &shared.External_Postgresql_SSL{
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return nil, errors.New("ERROR")
+				},
+			},
+
+			skipIpsList:   []string{},
+			ExpectedError: "ERROR",
+			wantError:     true,
+		},
+		{
+			description: "Error while fetching automate config",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				publicCert:  FileContent,
+				privateCert: FileContent,
+				rootCA:      FileContent,
+			},
+			infra: infra,
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return nil, errors.New("ERROR")
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP7: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Postgresql: &shared.External_Postgresql{
+											Ssl: &shared.External_Postgresql_SSL{
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{},
+			ExpectedError: "ERROR",
+			wantError:     true,
+		},
+		{
+			description: "same root ca and cn in one of the automate and chefserver ips ",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent,
+			},
+			infra:    infra,
+			nodesCn:  "chefnode",
+			flagsObj: &certRotateFlags{},
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnodee",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent + "a",
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+						ValidIP1: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP2: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+						ValidIP3: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnodee",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent + "a",
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{ValidIP1, ValidIP2},
+			ExpectedError: "",
+			wantError:     false,
+		},
+		{
+			description: "same rootca and cn in chefserver",
+			sshUtil:     GetMockSSHUtil(&SSHConfig{}, nil, completedMessage, nil, "", nil),
+			certs: &certificates{
+				rootCA: FileContent,
+			},
+			infra:    infra,
+			nodesCn:  "chefnode",
+			flagsObj: &certRotateFlags{},
+			MockPullConfigs: &MockPullConfigs{
+				pullAutomateConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnodee",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent + "a",
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+				pullChefServerConfigsFunc: func() (map[string]*deployment.AutomateConfig, error) {
+					return map[string]*deployment.AutomateConfig{
+						ValidIP2: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+						ValidIP3: {
+							Global: &shared.GlobalConfig{
+								V1: &shared.V1{
+									External: &shared.External{
+										Opensearch: &shared.External_Opensearch{
+											Ssl: &shared.External_Opensearch_SSL{
+												ServerName: &wrapperspb.StringValue{
+													Value: "chefnode",
+												},
+												RootCert: &wrapperspb.StringValue{
+													Value: FileContent,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					}, nil
+				},
+			},
+
+			skipIpsList:   []string{ValidIP2, ValidIP3},
+			ExpectedError: "",
+			wantError:     false,
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			c.pullConfigs = testCase.MockPullConfigs
+			skipIpsListGot, err := c.getSkipIpsListForOsRootCACNPatching(testCase.infra, testCase.sshUtil, testCase.certs, testCase.nodesCn, testCase.flagsObj)
+			if !testCase.wantError {
+				assert.Equal(t, testCase.skipIpsList, skipIpsListGot)
+			} else {
+				assert.Error(t, err)
+			}
+
+		})
+	}
+}
+
+func TestPatchConfig(t *testing.T) {
+	_, infra := getMockCertRotateFlowAndInfra()
+	type testCaseInfo struct {
+		description   string
+		param         *patchFnParameters
+		MockSSHUtil   sshutils.SSHUtil
+		isError       bool
+		ExpectedError string
+	}
+	testCases := []testCaseInfo{
+		{
+			description: "Test Case to rotate on backend",
+			param: &patchFnParameters{
+				sshUtil:       getMockSSHUtil(&SSHConfig{}, nil, "", nil),
+				config:        TestOpensearchAdminAndRootCA,
+				fileName:      "cert-rotate-os.toml",
+				timestamp:     time.Now().Format("20060102150405"),
+				remoteService: OPENSEARCH,
+				concurrent:    false,
+				infra:         infra,
+				flagsObj: &certRotateFlags{
+					opensearch: true,
+				},
+				skipIpsList: []string{},
+			},
+			MockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+			},
+			isError:       false,
+			ExpectedError: "",
+		},
+		{
+			description: "Test Case to rotate on frontend",
+			param: &patchFnParameters{
+				sshUtil:       getMockSSHUtil(&SSHConfig{}, nil, "", nil),
+				config:        TestFrontendConfig,
+				fileName:      "cert-rotate-fe.toml",
+				timestamp:     time.Now().Format("20060102150405"),
+				remoteService: AUTOMATE,
+				concurrent:    true,
+				infra:         infra,
+				flagsObj: &certRotateFlags{
+					automate: true,
+				},
+				skipIpsList: []string{},
+			},
+			MockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+			},
+			isError:       false,
+			ExpectedError: "",
+		},
+		{
+			description: "remote copying failed on on frontend",
+			param: &patchFnParameters{
+				config:        TestFrontendConfig,
+				fileName:      "cert-rotate-fe.toml",
+				timestamp:     time.Now().Format("20060102150405"),
+				remoteService: AUTOMATE,
+				concurrent:    true,
+				infra:         infra,
+				flagsObj: &certRotateFlags{
+					automate: true,
+				},
+				skipIpsList: []string{},
+			},
+			MockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  errors.New("remote copying failed on node"),
+							Output: "",
+						},
+					}
+				},
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+			},
+			isError:       true,
+			ExpectedError: "remote copying failed on node",
+		},
+		{
+			description: "remote execution failed on on frontend",
+			param: &patchFnParameters{
+				config:        TestFrontendConfig,
+				fileName:      "cert-rotate-fe.toml",
+				timestamp:     time.Now().Format("20060102150405"),
+				remoteService: AUTOMATE,
+				concurrent:    true,
+				infra:         infra,
+				flagsObj: &certRotateFlags{
+					automate: true,
+				},
+				skipIpsList: []string{},
+			},
+			MockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  errors.New("remote execution failed"),
+							Output: "",
+						},
+					}
+				},
+			},
+			isError:       false,
+			ExpectedError: "",
+		},
+		{
+			description: "Deployment service error on frontend",
+			param: &patchFnParameters{
+				config:        TestFrontendConfig,
+				fileName:      "cert-rotate-fe.toml",
+				timestamp:     time.Now().Format("20060102150405"),
+				remoteService: AUTOMATE,
+				concurrent:    true,
+				infra:         infra,
+				flagsObj: &certRotateFlags{
+					automate: true,
+				},
+				skipIpsList: []string{},
+			},
+			MockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
+				},
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "DeploymentServiceCallError",
+						},
+					}
+				},
+			},
+			isError:       false,
+			ExpectedError: "",
+		},
+		{
+			description: "Error occured while reading infra details on backend",
+			param: &patchFnParameters{
+				sshUtil:       getMockSSHUtil(&SSHConfig{}, nil, "", errors.Errorf("Error occured while reading infra details")),
+				config:        TestOpensearchAdminAndRootCA,
+				fileName:      "cert-rotate-os.toml",
+				timestamp:     time.Now().Format("20060102150405"),
+				remoteService: OPENSEARCH,
+				concurrent:    false,
+				infra:         infra,
+				flagsObj: &certRotateFlags{
+					opensearch: true,
+				},
+				skipIpsList: []string{},
+			},
+			isError:       true,
+			ExpectedError: "Error occured while reading infra details",
+		},
+	}
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			c := certRotateFlow{fileUtils: mockFS(),
+				sshUtil: testCase.MockSSHUtil,
+				writer:  getMockWriterImpl()}
+			testDir := t.TempDir()
+			fPath := filepath.Join(testDir, testCase.param.fileName)
+			testCase.param.fileName = fPath
+			output := c.patchConfig(testCase.param)
+			if testCase.isError {
+				assert.EqualError(t, output, testCase.ExpectedError)
+			} else {
+				assert.NoError(t, output)
+			}
+		})
+	}
+}
+
+func getMockCertRotateFlowAndInfra() (*certRotateFlow, *AutomateHAInfraDetails) {
+	return NewCertRotate(), NewMockInfra()
+}
+
+func NewMockInfra() *AutomateHAInfraDetails {
 	infra := &AutomateHAInfraDetails{}
 	infra.Outputs.AutomatePrivateIps.Value = []string{ValidIP, ValidIP1}
 	infra.Outputs.ChefServerPrivateIps.Value = []string{ValidIP2, ValidIP3}
 	infra.Outputs.OpensearchPrivateIps.Value = []string{ValidIP4, ValidIP5, ValidIP6}
 	infra.Outputs.PostgresqlPrivateIps.Value = []string{ValidIP7, ValidIP8, ValidIP9}
-	return c, infra
+	infra.Outputs.SSHUser.Value = "ubuntu"
+	infra.Outputs.SSHKeyFile.Value = "new.pem"
+	infra.Outputs.SSHPort.Value = "22"
+	return infra
+}
+
+func NewCertRotate() *certRotateFlow {
+	c := NewCertRotateFlow(mockFS(), &sshutils.MockSSHUtilsImpl{}, writer, &MockPullConfigs{})
+	return c
 }
 
 func mockFS() *fileutils.MockFileSystemUtils {
diff --git a/components/automate-cli/cmd/chef-automate/deployedConfig_test.go b/components/automate-cli/cmd/chef-automate/deployedConfig_test.go
index ab9e9838c..c317beed1 100644
--- a/components/automate-cli/cmd/chef-automate/deployedConfig_test.go
+++ b/components/automate-cli/cmd/chef-automate/deployedConfig_test.go
@@ -295,6 +295,10 @@ type MockPullConfigs struct {
 	getExceptionIpsFunc       func() []string
 	setExceptionIpsFunc       func(ips []string)
 	getOsCertsByIpFunc        func(map[string]*ConfigKeys) []CertByIP
+	setInfraAndSSHUtilFunc    func(*AutomateHAInfraDetails, SSHUtil)
+}
+
+func (m *MockPullConfigs) setInfraAndSSHUtil(*AutomateHAInfraDetails, SSHUtil) {
 }
 
 func (m *MockPullConfigs) fetchInfraConfig() (*ExistingInfraConfigToml, error) {
diff --git a/components/automate-cli/cmd/chef-automate/summary_test.go b/components/automate-cli/cmd/chef-automate/summary_test.go
index 25ffdd136..7ba3456ef 100644
--- a/components/automate-cli/cmd/chef-automate/summary_test.go
+++ b/components/automate-cli/cmd/chef-automate/summary_test.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"errors"
 	"fmt"
 	"strings"
 	"testing"
@@ -27,13 +28,17 @@ Incorrect postgresql IP, 127.0.0 IP address validation failed
 Incorrect postgresql IP, 127.0.1 IP address validation failed
 Incorrect postgresql IP, 127.0.2 IP address validation failed
 Incorrect postgresql IP, 127.0.3 IP address validation failed`
-	displayBE = `+------------+--------------+--------+--------------+-------------+---------+
-| NAME       | IP ADDRESS   | HEALTH | PROCESS      | UPTIME      | ROLE    |
-+------------+--------------+--------+--------------+-------------+---------+
-| postgresql | 198.51.100.7 | ERROR  | down (pid: ) | 0d 0h 0m 0s | Unknown |
-+------------+--------------+--------+--------------+-------------+---------+`
-	displayFEAutomate         = `| automate    | 198.51.100.1 | ERROR  | Unknown    |`
-	displayFECS               = `| chef-server | 198.51.100.2 | ERROR  | Unknown    |`
+	displayBE = `+------------+--------------+--------+--------------+-----+-------------+---------+
+| NAME       | IP ADDRESS   | HEALTH | PROCESS      | LAG | UPTIME      | ROLE    |
++------------+--------------+--------+--------------+-----+-------------+---------+
+| postgresql | 198.51.100.7 | ERROR  | down (pid: ) | NA  | 0d 0h 0m 0s | Unknown |
++------------+--------------+--------+--------------+-----+-------------+---------+`
+	pgFollowerNode = `+------------+--------------+--------+----------------+-----------------------+---------------+----------+
+| NAME       | IP ADDRESS   | HEALTH | PROCESS        | LAG                   | UPTIME        | ROLE     |
++------------+--------------+--------+----------------+-----------------------+---------------+----------+
+| postgresql | 198.51.100.7 | OK     | up (pid: 2760) | 0 bytes and 1 seconds |`
+	displayFEAutomate         = `| automate    | 198.51.100.1 | OK     | "green" (Active: 100.0) |`
+	displayFECS               = `| chef-server | 198.51.100.2 | OK     | "green" (Active: 100.0) |`
 	mockA2haHabitatAutoTfvars = "../../pkg/testfiles/a2ha_habitat.auto.tfvars"
 )
 
@@ -43,7 +48,11 @@ func TestCheckIPAddressesFromInfra(t *testing.T) {
 	infra.Outputs.ChefServerPrivateIps.Value = []string{ValidIP2, ValidIP3}
 	infra.Outputs.OpensearchPrivateIps.Value = []string{ValidIP4, ValidIP5, ValidIP6}
 	infra.Outputs.PostgresqlPrivateIps.Value = []string{ValidIP7, ValidIP8, ValidIP9}
-	ss := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, &StatusSummaryCmdFlags{}, getMockSSHUtilRunSummary(&SSHConfig{}, nil, nil))
+	ss := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, &StatusSummaryCmdFlags{}, &MockRemoteCmdExecutor{
+		ExecuteWithNodeMapFunc: func(nodeMap *NodeTypeAndCmd) (map[string][]*CmdResult, error) {
+			return nil, nil
+		},
+	})
 
 	automateIps, chefServerIps, opensearchIps, postgresqlIps, errList := ss.(*Summary).getIPAddressesFromFlagOrInfra()
 	assert.Equal(t, errList.Len(), 0)
@@ -66,7 +75,11 @@ func TestCheckIPAddressesByServicesAndIpFromFlag(t *testing.T) {
 		isChefServer: true,
 		isOpenSearch: true,
 		isPostgresql: true,
-	}, getMockSSHUtilRunSummary(&SSHConfig{}, nil, nil))
+	}, &MockRemoteCmdExecutor{
+		ExecuteWithNodeMapFunc: func(nodeMap *NodeTypeAndCmd) (map[string][]*CmdResult, error) {
+			return nil, nil
+		},
+	})
 
 	automateIps, chefServerIps, opensearchIps, postgresqlIps, errList := ss.(*Summary).getIPAddressesFromFlagOrInfra()
 	assert.Equal(t, errList.Len(), 0)
@@ -88,7 +101,11 @@ func TestCheckIPAddressesOnlyByServices(t *testing.T) {
 		isChefServer: true,
 		isOpenSearch: true,
 		isPostgresql: true,
-	}, getMockSSHUtilRunSummary(&SSHConfig{}, nil, nil))
+	}, &MockRemoteCmdExecutor{
+		ExecuteWithNodeMapFunc: func(nodeMap *NodeTypeAndCmd) (map[string][]*CmdResult, error) {
+			return nil, nil
+		},
+	})
 
 	automateIps, chefServerIps, opensearchIps, postgresqlIps, errList := ss.(*Summary).getIPAddressesFromFlagOrInfra()
 	assert.Equal(t, errList.Len(), 0)
@@ -111,7 +128,11 @@ func TestCheckIPAddressesError(t *testing.T) {
 		isChefServer: true,
 		isOpenSearch: true,
 		isPostgresql: true,
-	}, getMockSSHUtilRunSummary(&SSHConfig{}, nil, nil))
+	}, &MockRemoteCmdExecutor{
+		ExecuteWithNodeMapFunc: func(nodeMap *NodeTypeAndCmd) (map[string][]*CmdResult, error) {
+			return nil, nil
+		},
+	})
 
 	_, _, _, _, errList := ss.(*Summary).getIPAddressesFromFlagOrInfra()
 	assert.Equal(t, errList.Len(), 1)
@@ -131,7 +152,11 @@ func TestCheckIPAddressesValidation(t *testing.T) {
 		isChefServer: true,
 		isOpenSearch: true,
 		isPostgresql: true,
-	}, getMockSSHUtilRunSummary(&SSHConfig{}, nil, nil))
+	}, &MockRemoteCmdExecutor{
+		ExecuteWithNodeMapFunc: func(nodeMap *NodeTypeAndCmd) (map[string][]*CmdResult, error) {
+			return nil, nil
+		},
+	})
 
 	automateIps, chefServerIps, opensearchIps, postgresqlIps, errList := ss.(*Summary).getIPAddressesFromFlagOrInfra()
 	assert.Equal(t, errList.Len(), 16)
@@ -141,13 +166,57 @@ func TestCheckIPAddressesValidation(t *testing.T) {
 	assert.Equal(t, postgresqlIps, []string(nil))
 	assert.Contains(t, getSingleErrorFromList(errList).Error(), invalidIpAddress)
 }
+
+// {OsStatus 10.0.192.172 []  <nil>}
 func TestRunFENodeDiaplay(t *testing.T) {
 	a2haHabitatAutoTfvars = mockA2haHabitatAutoTfvars
 	infra := &AutomateHAInfraDetails{}
 	infra.Outputs.AutomatePrivateIps.Value = []string{ValidIP1}
 	infra.Outputs.ChefServerPrivateIps.Value = []string{ValidIP2}
-	sshUtil := getMockSSHUtilRunSummary(&SSHConfig{}, nil, nil)
-	ss := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, &StatusSummaryCmdFlags{}, sshUtil)
+	ss := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, &StatusSummaryCmdFlags{}, &MockRemoteCmdExecutor{
+		ExecuteWithNodeMapFunc: func(nodeMap *NodeTypeAndCmd) (map[string][]*CmdResult, error) {
+			if strings.Contains(strings.Join(nodeMap.Automate.CmdInputs.NodeIps, ","), ValidIP1) {
+
+				return map[string][]*CmdResult{
+					ValidIP1: {
+						&CmdResult{
+							ScriptName:  "OsStatus",
+							HostIP:      ValidIP1,
+							OutputFiles: []string{},
+							Error:       nil,
+							Output:      `{"cluster_name":"opensearch","status":"green","timed_out":false,"number_of_nodes":3,"number_of_data_nodes":3,"discovered_master":true,"active_primary_shards":35,"active_shards":74,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}`,
+						},
+						&CmdResult{
+							ScriptName:  "Status",
+							HostIP:      ValidIP1,
+							OutputFiles: []string{},
+							Error:       nil,
+							Output:      ``,
+						},
+					},
+				}, nil
+				// displayFEAutomate, nil
+			}
+			return map[string][]*CmdResult{
+				ValidIP2: {
+					&CmdResult{
+						ScriptName:  "OsStatus",
+						HostIP:      ValidIP2,
+						OutputFiles: []string{},
+						Error:       nil,
+						Output:      `{"cluster_name":"opensearch","status":"green","timed_out":false,"number_of_nodes":3,"number_of_data_nodes":3,"discovered_master":true,"active_primary_shards":35,"active_shards":74,"relocating_shards":0,"initializing_shards":0,"unassigned_shards":0,"delayed_unassigned_shards":0,"number_of_pending_tasks":0,"number_of_in_flight_fetch":0,"task_max_waiting_in_queue_millis":0,"active_shards_percent_as_number":100.0}`,
+					},
+					&CmdResult{
+						ScriptName:  "Status",
+						HostIP:      ValidIP2,
+						OutputFiles: []string{},
+						Error:       nil,
+						Output:      ``,
+					},
+				},
+			}, nil
+		},
+	})
 	err := ss.Prepare()
 	assert.NoError(t, err)
 	fe := ss.ShowFEStatus()
@@ -155,22 +224,6 @@ func TestRunFENodeDiaplay(t *testing.T) {
 	assert.Contains(t, fe, displayFECS)
 
 }
-func TestRunBENodeDiaplay(t *testing.T) {
-	a2haHabitatAutoTfvars = mockA2haHabitatAutoTfvars
-	infra := &AutomateHAInfraDetails{}
-	// infra.Outputs.OpensearchPrivateIps.Value = []string{ValidIP4, ValidIP5, ValidIP6}
-	infra.Outputs.PostgresqlPrivateIps.Value = []string{ValidIP7}
-	sshUtil := getMockSSHUtilRunSummary(&SSHConfig{hostIP: ValidIP7}, nil, nil)
-
-	ss := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, &StatusSummaryCmdFlags{}, sshUtil)
-	// Mock the time to a specific time
-	mockTime := time.Date(2023, 3, 15, 12, 0, 0, 0, time.UTC)
-	nowFunc = func() time.Time { return mockTime }
-	err := ss.Prepare()
-	assert.NoError(t, err)
-	be := ss.ShowBEStatus()
-	assert.Equal(t, be, displayBE)
-}
 
 func getMockSSHUtilRunSummary(sshConfig *SSHConfig, CFTRError error, CSECORError error) *MockSSHUtilsImpl {
 	return &MockSSHUtilsImpl{
@@ -266,3 +319,141 @@ func getMockSSHUtilRunSummary(sshConfig *SSHConfig, CFTRError error, CSECORError
 		},
 	}
 }
+
+func TestGetFollowerLag(t *testing.T) {
+	a2haHabitatAutoTfvars = mockA2haHabitatAutoTfvars
+
+	testCases := []struct {
+		description string
+		beOutput    map[string][]*CmdResult
+		expectedErr error
+		ip          string
+		wantErr     bool
+	}{
+		{
+			description: "Showing Lag with No Error for Follower node",
+			beOutput: map[string][]*CmdResult{
+				ValidIP7: {
+					&CmdResult{
+						ScriptName:  censusDetails,
+						HostIP:      ValidIP7,
+						OutputFiles: []string{},
+						Output: `{
+							"changed": false,
+							"census_groups": {
+								"automate-ha-postgresql.default": {
+									"population": {
+										"8cfc79a0be3c4e74ab717bac25a1d185": {
+											"leader": false,
+											"follower": true
+											}
+										}
+									}
+								}
+							}`,
+						Error: nil,
+					},
+					&CmdResult{
+						ScriptName:  defaultServiceDetails,
+						HostIP:      ValidIP7,
+						OutputFiles: []string{},
+						Output: `{
+							"process": { "pid": 2760, "state": "up", "state_entered": 1689602436 },
+							"sys": {
+								"member_id": "8cfc79a0be3c4e74ab717bac25a1d185"
+							}
+						}`,
+						Error: nil,
+					},
+					&CmdResult{
+						ScriptName:  defaultServiceHealthDetails,
+						HostIP:      ValidIP7,
+						OutputFiles: []string{},
+						Output:      `{"status":"OK","stdout":"Primary server is available at 10.0.192.111 with uptime: 0s\nLocal replica 10.0.192.209 is 0 bytes and 1 seconds behind the primary\n","stderr":""}`,
+						Error:       nil,
+					},
+				},
+			},
+			ip:          ValidIP7,
+			expectedErr: nil,
+			wantErr:     false,
+		},
+		{
+			description: "Showing Lag with Parsing Error",
+			beOutput: map[string][]*CmdResult{
+				ValidIP7: {
+					&CmdResult{
+						ScriptName:  censusDetails,
+						HostIP:      ValidIP7,
+						OutputFiles: []string{},
+						Output: `{
+							"changed": false,
+							"census_groups": {
+								"automate-ha-postgresql.default": {
+									"population": {
+										"8cfc79a0be3c4e74ab717bac25a1d185": {
+											"leader": false,
+											"follower": true
+											
+										}
+									}
+								}
+							}`,
+						Error: nil,
+					},
+					&CmdResult{
+						ScriptName:  defaultServiceDetails,
+						HostIP:      ValidIP7,
+						OutputFiles: []string{},
+						Output: `{
+							"process": { "pid": 2760, "state": "up", "state_entered": 1689602436 ,
+							"sys": {
+								"member_id": "8cfc79a0be3c4e74ab717bac25a1d185"
+							}
+						}`,
+						Error: nil,
+					},
+					&CmdResult{
+						ScriptName:  defaultServiceHealthDetails,
+						HostIP:      ValidIP7,
+						OutputFiles: []string{},
+						Output:      `{"status":"OK","stdout":"Primary server is available at 10.0.192.111 with uptime: 0s\nLocal replica 10.0.192.209 is 0 bytes and 1 seconds behind the primary\n","stderr":"}`,
+						Error:       nil,
+					},
+				},
+			},
+			ip:          ValidIP7,
+			expectedErr: errors.New("ERROR"),
+			wantErr:     true,
+		},
+	}
+
+	for _, test := range testCases {
+
+		infra := &AutomateHAInfraDetails{}
+		infra.Outputs.PostgresqlPrivateIps.Value = []string{test.ip}
+		ss := NewStatusSummary(infra, FeStatus{}, BeStatus{}, 10, time.Second, &StatusSummaryCmdFlags{
+			node:         test.ip,
+			isPostgresql: true,
+		}, &MockRemoteCmdExecutor{
+			ExecuteWithNodeMapFunc: func(nodeMap *NodeTypeAndCmd) (map[string][]*CmdResult, error) {
+				return test.beOutput, nil
+			},
+		})
+
+		err := ss.Prepare()
+		assert.NoError(t, err)
+		be := ss.ShowBEStatus()
+
+		if test.wantErr {
+			assert.Contains(t, be, ValidIP7)
+			assert.Contains(t, be, test.expectedErr.Error())
+
+		} else {
+			assert.Contains(t, be, ValidIP7)
+			assert.Contains(t, be, "0 bytes and 1 seconds")
+			assert.Contains(t, be, "up (pid: 2760)")
+			assert.Contains(t, be, "postgresql")
+		}
+	}
+}
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/batchcheckservice_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/batchcheckservice_test.go
index 151564e98..343e54a58 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/batchcheckservice_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/batchcheckservice_test.go
@@ -385,10 +385,10 @@ func TestBatchCheckService(t *testing.T) {
 			},
 			expectedResponseForAutomateIp:    "{\"node_type\":\"automate\",\"ip\":\"1.2.3.4\",\"tests\":[{\"passed\":false,\"msg\":\"SSH User Access Check\",\"check\":\"ssh-user\",\"checks\":[{\"title\":\"ssh-user-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"ssh-user-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":false,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Certificate Check\",\"check\":\"certificate\",\"checks\":[{\"title\":\"certificate-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"certificate-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
 			expectedResponseFromChefServerIp: "{\"node_type\":\"chef-infra-server\",\"ip\":\"1.2.3.4\",\"tests\":[{\"passed\":true,\"msg\":\"SSH User Access Check\",\"check\":\"ssh-user\",\"checks\":[{\"title\":\"ssh-user-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"ssh-user-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Certificate Check\",\"check\":\"certificate\",\"checks\":[{\"title\":\"certificate-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"certificate-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForPostgresIp1:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.7\",\"tests\":[{\"passed\":false,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":false,\"msg\":\"External Postgresql Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForPostgresIp2:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.8\",\"tests\":[{\"passed\":false,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":true},{\"passed\":true,\"msg\":\"External Postgresql Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForOpenSearchIp1: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.5\",\"tests\":[{\"passed\":false,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":false,\"msg\":\"External Opensearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForOpenSearchIp2: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.6\",\"tests\":[{\"passed\":true,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"External Opensearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForPostgresIp1:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.7\",\"tests\":[{\"passed\":false,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":false,\"msg\":\"External PostgreSQL Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForPostgresIp2:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.8\",\"tests\":[{\"passed\":false,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":true},{\"passed\":true,\"msg\":\"External PostgreSQL Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForOpenSearchIp1: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.5\",\"tests\":[{\"passed\":false,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":false,\"msg\":\"External OpenSearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForOpenSearchIp2: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.6\",\"tests\":[{\"passed\":true,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-count-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-count-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"External OpenSearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
 		},
 		{
 			description:   "Batch check server success with nodes on different ip",
@@ -541,10 +541,10 @@ func TestBatchCheckService(t *testing.T) {
 			},
 			expectedResponseForAutomateIp:    "{\"node_type\":\"automate\",\"ip\":\"1.2.3.4\",\"tests\":[{\"passed\":true,\"msg\":\"SSH User Access Check\",\"check\":\"ssh-user\",\"checks\":[{\"title\":\"ssh-user-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"ssh-user-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Certificate Check\",\"check\":\"certificate\",\"checks\":[{\"title\":\"certificate-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"certificate-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
 			expectedResponseFromChefServerIp: "{\"node_type\":\"chef-infra-server\",\"ip\":\"1.2.8.4\",\"tests\":[{\"passed\":true,\"msg\":\"SSH User Access Check\",\"check\":\"ssh-user\",\"checks\":[{\"title\":\"ssh-user-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"ssh-user-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForPostgresIp1:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.7\",\"tests\":[{\"passed\":true,\"msg\":\"External Postgresql Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForPostgresIp2:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.8\",\"tests\":[{\"passed\":true,\"msg\":\"External Postgresql Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForOpenSearchIp1: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.5\",\"tests\":[{\"passed\":true,\"msg\":\"External Opensearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForOpenSearchIp2: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.6\",\"tests\":[{\"passed\":true,\"msg\":\"External Opensearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForPostgresIp1:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.7\",\"tests\":[{\"passed\":true,\"msg\":\"External PostgreSQL Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForPostgresIp2:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.8\",\"tests\":[{\"passed\":true,\"msg\":\"External PostgreSQL Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForOpenSearchIp1: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.5\",\"tests\":[{\"passed\":true,\"msg\":\"External OpenSearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForOpenSearchIp2: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.6\",\"tests\":[{\"passed\":true,\"msg\":\"External OpenSearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
 		},
 		{
 			description:                          "Batch check service returns error mock server in not started successfully on all nodes",
@@ -708,10 +708,10 @@ func TestBatchCheckService(t *testing.T) {
 			},
 			expectedResponseForAutomateIp:    "{\"node_type\":\"automate\",\"ip\":\"1.2.3.4\",\"tests\":[{\"passed\":false,\"msg\":\"SSH User Access Check\",\"check\":\"ssh-user\",\"checks\":[{\"title\":\"ssh-user-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"ssh-user-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[],\"error\":{\"code\":503,\"message\":\"Error while Performing Hardware resource count check from batch Check API\"},\"skipped\":false},{\"passed\":true,\"msg\":\"Certificate Check\",\"check\":\"certificate\",\"checks\":[{\"title\":\"certificate-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"certificate-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
 			expectedResponseFromChefServerIp: "{\"node_type\":\"chef-infra-server\",\"ip\":\"1.2.8.4\",\"tests\":[{\"passed\":true,\"msg\":\"SSH User Access Check\",\"check\":\"ssh-user\",\"checks\":[{\"title\":\"ssh-user-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"ssh-user-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false},{\"passed\":true,\"msg\":\"Hardware Resource Count Check\",\"check\":\"hardware-resource-count\",\"checks\":[{\"title\":\"hardware-resource-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"hardware-resource-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForPostgresIp1:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.7\",\"tests\":[{\"passed\":false,\"msg\":\"External Postgresql Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForPostgresIp2:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.8\",\"tests\":[{\"passed\":true,\"msg\":\"External Postgresql Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForOpenSearchIp1: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.5\",\"tests\":[{\"passed\":false,\"msg\":\"External Opensearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
-			expectedResponseForOpenSearchIp2: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.6\",\"tests\":[{\"passed\":true,\"msg\":\"External Opensearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForPostgresIp1:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.7\",\"tests\":[{\"passed\":false,\"msg\":\"External PostgreSQL Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForPostgresIp2:   "{\"node_type\":\"postgresql\",\"ip\":\"1.2.3.8\",\"tests\":[{\"passed\":true,\"msg\":\"External PostgreSQL Check\",\"check\":\"external-postgresql\",\"checks\":[{\"title\":\"external-postgresql-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-postgresql-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForOpenSearchIp1: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.5\",\"tests\":[{\"passed\":false,\"msg\":\"External OpenSearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
+			expectedResponseForOpenSearchIp2: "{\"node_type\":\"opensearch\",\"ip\":\"1.2.3.6\",\"tests\":[{\"passed\":true,\"msg\":\"External OpenSearch Check\",\"check\":\"external-opensearch\",\"checks\":[{\"title\":\"external-opensearch-check-1\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false},{\"title\":\"external-opensearch-check-2\",\"passed\":false,\"success_msg\":\"\",\"error_msg\":\"\",\"resolution_msg\":\"\",\"skipped\":false}],\"skipped\":false}]}",
 		},
 	}
 
@@ -1117,20 +1117,20 @@ func TestConstructResult(t *testing.T) {
 	result = constructResult(ipMap)
 	assert.Len(t, result, 2, "Result should have two items")
 
-	for _, v := range result {
+	for i, v := range result {
 		// Check the nodeType1
 		if v.NodeType == "nodeType1" {
-			assert.Equal(t, "host1", result[0].Ip, "IP should be 'host1'")
-			assert.Len(t, result[0].Tests, 1, "Tests should have one item")
-			assert.Equal(t, true, result[0].Tests[0].Passed, "Passed should be true")
-			assert.Equal(t, "Test message 1", result[0].Tests[0].Message, "Message should be 'Test message 1'")
+			assert.Equal(t, "host1", result[i].Ip, "IP should be 'host1'")
+			assert.Len(t, result[i].Tests, 1, "Tests should have one item")
+			assert.Equal(t, true, result[i].Tests[0].Passed, "Passed should be true")
+			assert.Equal(t, "Test message 1", result[i].Tests[0].Message, "Message should be 'Test message 1'")
 		}
 		if v.NodeType == "nodeType2" {
-			assert.Equal(t, "host2", result[1].Ip, "IP should be 'host2'")
-			assert.Equal(t, "nodeType2", result[1].NodeType, "NodeType should be 'nodeType2'")
-			assert.Len(t, result[1].Tests, 1, "Tests should have one item")
-			assert.Equal(t, true, result[1].Tests[0].Passed, "Passed should be true")
-			assert.Equal(t, "Test message 2", result[1].Tests[0].Message, "Message should be 'Test message 2'")
+			assert.Equal(t, "host2", result[i].Ip, "IP should be 'host2'")
+			assert.Equal(t, "nodeType2", result[i].NodeType, "NodeType should be 'nodeType2'")
+			assert.Len(t, result[i].Tests, 1, "Tests should have one item")
+			assert.Equal(t, true, result[i].Tests[0].Passed, "Passed should be true")
+			assert.Equal(t, "Test message 2", result[i].Tests[0].Message, "Message should be 'Test message 2'")
 		}
 
 	}
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger_test.go
index 8c6ba9f50..f1cbc356e 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/certificatechecktrigger/certificatechecktrigger_test.go
@@ -670,18 +670,20 @@ func Test_skipCertificateForAutomateAndChefServerNodes(t *testing.T) {
 				NodeType:  constants.AUTOMATE,
 				CheckType: constants.CERTIFICATE,
 				Result: models.ApiResult{
-					Skipped: true,
-					Passed:  false,
-					Check:   constants.CERTIFICATE,
+					Skipped:     true,
+					Passed:      false,
+					Check:       constants.CERTIFICATE,
+					SkipMessage: constants.SKIP_CERT_TEST_MESSAGE,
 				},
 			}, {
 				Host:      "test_ip2",
 				NodeType:  constants.AUTOMATE,
 				CheckType: constants.CERTIFICATE,
 				Result: models.ApiResult{
-					Skipped: true,
-					Passed:  false,
-					Check:   constants.CERTIFICATE,
+					Skipped:     true,
+					Passed:      false,
+					Check:       constants.CERTIFICATE,
+					SkipMessage: constants.SKIP_CERT_TEST_MESSAGE,
 				},
 			},
 		},
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchchecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchchecktrigger_test.go
index 8d3718069..ceea47189 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchchecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalopensearchchecktrigger/externalopensearchchecktrigger_test.go
@@ -310,7 +310,7 @@ func TestOpensearchCheck_Run(t *testing.T) {
 				},
 			},
 			httpStatusResponse: `{"error":{"code":400, "message":"opensearch_domain_name, opensearch_domain_url, opensearch_username, opensearch_user_password or opensearch_root_cert cannot be empty"}}`,
-			response: "opensearch_domain_name, opensearch_domain_url, opensearch_username, opensearch_user_password or opensearch_root_cert cannot be empty",
+			response:           "opensearch_domain_name, opensearch_domain_url, opensearch_username, opensearch_user_password or opensearch_root_cert cannot be empty",
 		},
 		{
 			name:           "Gateway Timeout",
@@ -334,7 +334,7 @@ func TestOpensearchCheck_Run(t *testing.T) {
 				},
 			},
 			httpStatusResponse: `{"error":{"code":504,"message":"context deadline exceeded"}}`,
-			response: "context deadline exceeded",
+			response:           "context deadline exceeded",
 		},
 		{
 			name:     "Empty OS",
@@ -420,6 +420,7 @@ func TestOpensearchCheck_Run(t *testing.T) {
 					assert.Equal(t, "127.0.0.1", got[0].Host)
 					assert.Equal(t, constants.AUTOMATE, got[0].NodeType)
 					assert.True(t, got[0].Result.Skipped)
+					assert.Equal(t, constants.SKIP_MANAGED_OS_TEST_MESSAGE, got[0].Result.SkipMessage)
 				} else {
 					assert.Nil(t, got[0].Result.Error)
 					assert.Equal(t, constants.LOCALHOST, got[0].Host)
@@ -450,7 +451,7 @@ func TestForChefserverOpensearch(t *testing.T) {
 			},
 		}
 		isError := false
-		server, host, port := createDummyServer(t, http.StatusOK, true,"")
+		server, host, port := createDummyServer(t, http.StatusOK, true, "")
 		defer server.Close()
 		svc := NewExternalOpensearchCheck(
 			logger.NewLogrusStandardLogger(),
@@ -494,7 +495,7 @@ func TestForChefserverOpensearch(t *testing.T) {
 			},
 		}
 		isError := false
-		server, host, port := createDummyServer(t, http.StatusOK, false,"")
+		server, host, port := createDummyServer(t, http.StatusOK, false, "")
 		defer server.Close()
 		svc := NewExternalOpensearchCheck(
 			logger.NewLogrusStandardLogger(),
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlchecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlchecktrigger_test.go
index cfad2c895..7b863f881 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlchecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/externalpostgresqlchecktrigger/externalpostgresqlchecktrigger_test.go
@@ -311,7 +311,7 @@ func TestPostgresCheckAutomate_Run(t *testing.T) {
 					},
 				},
 			},
-			response: "Internal Server Error",
+			response:               "Internal Server Error",
 			requiredStatusResponse: `{"error":{"code":500,"message":"Internal Server Error"}}`,
 		},
 		{
@@ -335,7 +335,7 @@ func TestPostgresCheckAutomate_Run(t *testing.T) {
 					},
 				},
 			},
-			response: "context deadline exceeded",
+			response:               "context deadline exceeded",
 			requiredStatusResponse: `{"error":{"code":504,"message":"context deadline exceeded"}}`,
 		},
 		{
@@ -418,6 +418,7 @@ func TestPostgresCheckAutomate_Run(t *testing.T) {
 					assert.Equal(t, "127.0.0.1", got[0].Host)
 					assert.Equal(t, constants.AUTOMATE, got[0].NodeType)
 					assert.True(t, got[0].Result.Skipped)
+					assert.Equal(t, constants.SKIP_MANAGED_PG_TEST_MESSAGE, got[0].Result.SkipMessage)
 				} else {
 					assert.Nil(t, got[0].Result.Error)
 					assert.Equal(t, constants.LOCALHOST, got[0].Host)
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck_test.go
index dac569b1e..77335cf19 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/fqdnchecktrigger/fqdncheck_test.go
@@ -501,6 +501,7 @@ func TestFqdnCheck_Run(t *testing.T) {
 					assert.Len(t, got, 2)
 					assert.True(t, got[0].Result.Skipped)
 					assert.True(t, got[1].Result.Skipped)
+					assert.Equal(t, constants.SKIP_CS_FQDN_TEST_MESSAGE, got[1].Result.SkipMessage)
 				} else {
 					assert.Len(t, got, tt.args.config.Hardware.ChefInfraServerNodeCount)
 					assert.NotNil(t, got[0].Result.Error)
@@ -513,7 +514,9 @@ func TestFqdnCheck_Run(t *testing.T) {
 					assert.NotNil(t, got)
 					assert.Len(t, got, 2)
 					assert.True(t, got[0].Result.Skipped)
+					assert.Equal(t, constants.SKIP_MISSING_HARDWARE_MESSAGE, got[0].Result.SkipMessage)
 					assert.True(t, got[1].Result.Skipped)
+					assert.Equal(t, constants.SKIP_MISSING_HARDWARE_MESSAGE, got[1].Result.SkipMessage)
 				} else {
 					assert.Equal(t, got, want)
 					assert.NotNil(t, got)
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 6071c13ce..c2455b543 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
@@ -295,7 +295,7 @@ func TestHardwareResourceCountCheck_TriggerHardwareResourceCountCheck(t *testing
 	t.Run("cannot reach", func(t *testing.T) {
 		// create the CheckTrigger instance to be tested
 		hrc := HardwareResourceCountCheck{log: logger.NewTestLogger(), httpRequestClient: &httputils.MockHTTPClient{
-			func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
+			MakeRequestFunc : func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
 				return nil, []byte(""), errors.New("")
 			},
 		},host: "invalid-url"}
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 ca9e27b92..737957b45 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
@@ -635,7 +635,7 @@ func TestNfsBackupConfigCheck_Run(t *testing.T) {
 			name: "Passed Response for chef-server,automate,opensearch and postgresql",
 			args: args{
 				config: &models.Config{
-					Hardware:       hardware,
+					Hardware: hardware,
 					Backup: &models.Backup{
 						FileSystem: &models.FileSystem{
 							MountLocation: mountLocation,
@@ -654,7 +654,7 @@ func TestNfsBackupConfigCheck_Run(t *testing.T) {
 			name: "Failure Mount Response for chef-server,automate,opensearch and postgresql",
 			args: args{
 				config: &models.Config{
-					Hardware:       hardware,
+					Hardware: hardware,
 					Backup: &models.Backup{
 						FileSystem: &models.FileSystem{
 							MountLocation: mountLocation,
@@ -674,7 +674,7 @@ func TestNfsBackupConfigCheck_Run(t *testing.T) {
 			name: "Recived Internal Server Error From the API",
 			args: args{
 				config: &models.Config{
-					Hardware:       hardware,
+					Hardware: hardware,
 					Backup: &models.Backup{
 						FileSystem: &models.FileSystem{
 							MountLocation: mountLocation,
@@ -693,7 +693,7 @@ func TestNfsBackupConfigCheck_Run(t *testing.T) {
 			name: "Invalid Response from service",
 			args: args{
 				config: &models.Config{
-					Hardware:       hardware,
+					Hardware: hardware,
 					Backup: &models.Backup{
 						FileSystem: &models.FileSystem{
 							MountLocation: mountLocation,
@@ -852,6 +852,7 @@ func TestHardwareResourceCountCheck_TriggerHardwareResourceCountCheck(t *testing
 			assert.Equal(t, constants.NFS_BACKUP_CONFIG, v.CheckType)
 			assert.Equal(t, constants.NFS_BACKUP_CONFIG, v.Result.Check)
 			assert.True(t, v.Result.Skipped)
+			assert.Equal(t, constants.SKIP_MISSING_HARDWARE_MESSAGE, v.Result.SkipMessage)
 		}
 	})
 
@@ -883,6 +884,7 @@ func TestHardwareResourceCountCheck_TriggerHardwareResourceCountCheck(t *testing
 			assert.Equal(t, constants.NFS_BACKUP_CONFIG, v.CheckType)
 			assert.Equal(t, constants.NFS_BACKUP_CONFIG, v.Result.Check)
 			assert.True(t, v.Result.Skipped)
+			assert.Equal(t, constants.SKIP_BACKUP_TEST_MESSAGE_NFS, v.Result.SkipMessage)
 		}
 	})
 	t.Run("Empty FileSystem", func(t *testing.T) {
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 ea59d5838..248a516fc 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
@@ -10,9 +10,9 @@ import (
 
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
+	cfg "github.com/chef/automate/lib/config"
 	"github.com/chef/automate/lib/logger"
 	"github.com/stretchr/testify/assert"
-	cfg "github.com/chef/automate/lib/config"
 )
 
 var (
@@ -246,6 +246,7 @@ func TestOpensearchS3BucketAccessCheck_Run(t *testing.T) {
 					assert.Equal(t, constants.OPENSEARCH, got[0].NodeType)
 					assert.Equal(t, constants.AWS_OPENSEARCH_S3_BUCKET_ACCESS, got[0].CheckType)
 					assert.True(t, got[0].Result.Skipped)
+					assert.Equal(t, constants.SKIP_OS_BUCKET_TEST_MESSAGE, got[0].Result.SkipMessage)
 				} else if tt.name == "Empty OS and Object storage" {
 					assert.Len(t, got, 1)
 					assert.Equal(t, "dave.com", got[0].Host)
@@ -261,6 +262,7 @@ func TestOpensearchS3BucketAccessCheck_Run(t *testing.T) {
 					assert.Equal(t, constants.OPENSEARCH, got[0].NodeType)
 					assert.Equal(t, constants.AWS_OPENSEARCH_S3_BUCKET_ACCESS, got[0].CheckType)
 					assert.True(t, got[0].Result.Skipped)
+					assert.Equal(t, constants.SKIP_OS_BUCKET_TEST_SELF_MANAGED_MESSAGE, got[0].Result.SkipMessage)
 				} else {
 					assert.Equal(t, want, got)
 					assert.NotNil(t, got)
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger_test.go
index bdd80277c..cb6bc35e2 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/s3backupchecktrigger/s3backupconfigchecktrigger_test.go
@@ -8,11 +8,10 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/stretchr/testify/assert"
-
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
 	"github.com/chef/automate/lib/logger"
+	"github.com/stretchr/testify/assert"
 )
 
 const (
@@ -264,7 +263,7 @@ func TestS3BackupConfigCheck_Run(t *testing.T) {
 					},
 				},
 			},
-			response: "Internal Server Error",
+			response:               "Internal Server Error",
 			requiredStatusResponse: `{"error":{"code":500,"message":"Internal Server Error"}}`,
 		},
 		{
@@ -291,7 +290,7 @@ func TestS3BackupConfigCheck_Run(t *testing.T) {
 				},
 			},
 			requiredStatusResponse: `{"error":{"code":504,"message":"context deadline exceeded"}}`,
-			response: "context deadline exceeded",
+			response:               "context deadline exceeded",
 		},
 	}
 	for _, tt := range tests {
@@ -345,6 +344,7 @@ func TestRunS3BackupCheck(t *testing.T) {
 		assert.Equal(t, constants.AUTOMATE, got[0].NodeType)
 		assert.Equal(t, constants.S3_BACKUP_CONFIG, got[0].CheckType)
 		assert.True(t, got[0].Result.Skipped)
+		assert.Equal(t, constants.SKIP_MISSING_HARDWARE_MESSAGE, got[0].Result.SkipMessage)
 
 	})
 
@@ -428,3 +428,60 @@ func TestGetPortsForMockServer(t *testing.T) {
 
 	assert.Equal(t, 0, len(resp))
 }
+
+func TestS3ConfigSkippedResponse(t *testing.T) {
+	type args struct {
+		config    *models.Config
+		checkType string
+	}
+	tests := []struct {
+		name string
+		args args
+		want []models.CheckTriggerResponse
+	}{
+		{
+			name: "Make the skip Response",
+			args: args{
+				config: &models.Config{
+					Hardware: &models.Hardware{
+						AutomateNodeCount:        1,
+						AutomateNodeIps:          []string{constants.LOCALHOST},
+						ChefInfraServerNodeCount: 1,
+						ChefInfraServerNodeIps:   []string{constants.LOCALHOST},
+					},
+				},
+				checkType: "s3-backup-config",
+			},
+			want: []models.CheckTriggerResponse{
+				{
+					NodeType:  "automate",
+					CheckType: "s3-backup-config",
+					Result: models.ApiResult{
+						Passed:      false,
+						Skipped:     true,
+						Check:       "s3-backup-config",
+						SkipMessage: constants.SKIP_BACKUP_TEST_MESSAGE_S3,
+					},
+					Host: constants.LOCALHOST,
+				},
+				{
+					NodeType:  "chef-infra-server",
+					CheckType: "s3-backup-config",
+					Result: models.ApiResult{
+						Passed:      false,
+						Skipped:     true,
+						Check:       "s3-backup-config",
+						SkipMessage: constants.SKIP_BACKUP_TEST_MESSAGE_S3,
+					},
+					Host: constants.LOCALHOST,
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := s3ConfigSkippedResponse(tt.args.config, tt.args.checkType, constants.SKIP_BACKUP_TEST_MESSAGE_S3)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go
index 5e056ceff..be31cca5a 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go
@@ -365,6 +365,7 @@ func TestSshUserAccessCheck_Run(t *testing.T) {
 		assert.Equal(t, constants.CHEF_INFRA_SERVER, got[1].NodeType)
 		assert.Equal(t, constants.SSH_USER, got[1].CheckType)
 		assert.True(t, got[0].Result.Skipped)
+		assert.Equal(t, constants.SKIP_MISSING_HARDWARE_MESSAGE, got[0].Result.SkipMessage)
 	})
 	t.Run("Nil SSHUser", func(t *testing.T) {
 		config := &models.Config{
@@ -389,6 +390,7 @@ func TestSshUserAccessCheck_Run(t *testing.T) {
 			assert.Equal(t, constants.SSH_USER, v.Result.Check)
 			assert.Nil(t, v.Result.Error)
 			assert.True(t, v.Result.Skipped)
+			assert.Equal(t, constants.SKIP_SSH_USER_TEST_MESSAGE, v.Result.SkipMessage)
 		}
 	})
 	t.Run("Empty SSHUser", func(t *testing.T) {
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi_test.go
index 628042315..caad16069 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/triggerapi_test.go
@@ -216,10 +216,10 @@ func TestTriggerCheckAPI(t *testing.T) {
 		}))
 		defer server.Close()
 		requestBody := models.SshUserChecksRequest{
-			Ip: "1.1.1.1",
-			UserName: "admin",
-			Port: "22",
-			PrivateKey: "",
+			Ip:           "1.1.1.1",
+			UserName:     "admin",
+			Port:         "22",
+			PrivateKey:   "",
 			SudoPassword: "",
 		}
 		output := make(chan models.CheckTriggerResponse)
@@ -508,9 +508,10 @@ func TestNilResp(t *testing.T) {
 			NodeType:  constants.AUTOMATE,
 			CheckType: checkType,
 			Result: models.ApiResult{
-				Passed:  false,
-				Skipped: true,
-				Check:   checkType,
+				Passed:      false,
+				Skipped:     true,
+				SkipMessage: constants.SKIP_MISSING_HARDWARE_MESSAGE,
+				Check:       checkType,
 			},
 			Host: constants.UNKNOWN_HOST,
 		},
@@ -518,15 +519,16 @@ func TestNilResp(t *testing.T) {
 			NodeType:  constants.CHEF_INFRA_SERVER,
 			CheckType: checkType,
 			Result: models.ApiResult{
-				Passed:  false,
-				Skipped: true,
-				Check:   checkType,
+				Passed:      false,
+				Skipped:     true,
+				SkipMessage: constants.SKIP_MISSING_HARDWARE_MESSAGE,
+				Check:       checkType,
 			},
 			Host: constants.UNKNOWN_HOST,
 		},
 	}
 
-	result1 := HardwareNil(checkType, false, false, false)
+	result1 := HardwareNil(checkType, constants.SKIP_MISSING_HARDWARE_MESSAGE, false, false, false)
 	assert.Equal(t, expected1, result1)
 
 	// Test case 2: Include OPENSEARCH
@@ -534,14 +536,15 @@ func TestNilResp(t *testing.T) {
 		NodeType:  constants.OPENSEARCH,
 		CheckType: checkType,
 		Result: models.ApiResult{
-			Passed:  false,
-			Skipped: true,
-			Check:   checkType,
+			Passed:      false,
+			Skipped:     true,
+			SkipMessage: constants.SKIP_MISSING_HARDWARE_MESSAGE,
+			Check:       checkType,
 		},
 		Host: constants.UNKNOWN_HOST,
 	})
 
-	result2 := HardwareNil(checkType, true, false, false)
+	result2 := HardwareNil(checkType, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, false, false)
 	assert.Equal(t, expected2, result2)
 
 	// Test case 3: Include POSTGRESQL and BASTION
@@ -549,23 +552,25 @@ func TestNilResp(t *testing.T) {
 		NodeType:  constants.POSTGRESQL,
 		CheckType: checkType,
 		Result: models.ApiResult{
-			Passed:  false,
-			Skipped: true,
-			Check:   checkType,
+			Passed:      false,
+			Skipped:     true,
+			SkipMessage: constants.SKIP_MISSING_HARDWARE_MESSAGE,
+			Check:       checkType,
 		},
 		Host: constants.UNKNOWN_HOST,
 	}, models.CheckTriggerResponse{
 		NodeType:  constants.BASTION,
 		CheckType: checkType,
 		Result: models.ApiResult{
-			Passed:  false,
-			Skipped: true,
-			Check:   checkType,
+			Passed:      false,
+			Skipped:     true,
+			SkipMessage: constants.SKIP_MISSING_HARDWARE_MESSAGE,
+			Check:       checkType,
 		},
 		Host: constants.LOCALHOST,
 	})
 
-	result3 := HardwareNil(checkType, true, true, true)
+	result3 := HardwareNil(checkType, constants.SKIP_MISSING_HARDWARE_MESSAGE, true, true, true)
 	assert.Equal(t, expected3, result3)
 }
 
@@ -603,14 +608,15 @@ func TestGetSkippedTriggerCheckResp(t *testing.T) {
 		NodeType:  nodeType,
 		CheckType: checkType,
 		Result: models.ApiResult{
-			Passed:  false,
-			Skipped: true,
-			Check:   checkType,
+			Passed:      false,
+			Skipped:     true,
+			Check:       checkType,
+			SkipMessage: "test",
 		},
 		Host: ip,
 	}
 
-	result := SkippedTriggerCheckResp(ip, checkType, nodeType)
+	result := SkippedTriggerCheckResp(ip, checkType, nodeType, "test")
 
 	assert.Equal(t, expected, result)
 }
@@ -627,19 +633,20 @@ func TestGetNilResp(t *testing.T) {
 	}
 
 	checkType := "sampleCheckType"
+	exampleSkip := "skip test message"
 
 	expectedResponses := []models.CheckTriggerResponse{
-		SkippedTriggerCheckResp("1.2.3.4", checkType, constants.AUTOMATE),
-		SkippedTriggerCheckResp("5.6.7.8", checkType, constants.AUTOMATE),
-		SkippedTriggerCheckResp("10.20.30.40", checkType, constants.CHEF_INFRA_SERVER),
-		SkippedTriggerCheckResp("50.60.70.80", checkType, constants.CHEF_INFRA_SERVER),
-		SkippedTriggerCheckResp("100.200.300.400", checkType, constants.POSTGRESQL),
-		SkippedTriggerCheckResp("500.600.700.800", checkType, constants.POSTGRESQL),
-		SkippedTriggerCheckResp("192.168.1.1", checkType, constants.OPENSEARCH),
-		SkippedTriggerCheckResp("192.168.1.2", checkType, constants.OPENSEARCH),
+		SkippedTriggerCheckResp("1.2.3.4", checkType, constants.AUTOMATE, exampleSkip),
+		SkippedTriggerCheckResp("5.6.7.8", checkType, constants.AUTOMATE, exampleSkip),
+		SkippedTriggerCheckResp("10.20.30.40", checkType, constants.CHEF_INFRA_SERVER, exampleSkip),
+		SkippedTriggerCheckResp("50.60.70.80", checkType, constants.CHEF_INFRA_SERVER, exampleSkip),
+		SkippedTriggerCheckResp("100.200.300.400", checkType, constants.POSTGRESQL, exampleSkip),
+		SkippedTriggerCheckResp("500.600.700.800", checkType, constants.POSTGRESQL, exampleSkip),
+		SkippedTriggerCheckResp("192.168.1.1", checkType, constants.OPENSEARCH, exampleSkip),
+		SkippedTriggerCheckResp("192.168.1.2", checkType, constants.OPENSEARCH, exampleSkip),
 	}
 
-	responses := ConstructNilResp(config, checkType)
+	responses := ConstructNilResp(config, checkType, exampleSkip)
 
 	assert.Equal(t, expectedResponses, responses)
 }
diff --git a/lib/config/hadeployconfigverify_test.go b/lib/config/hadeployconfigverify_test.go
index 22ae5b900..087e584b8 100644
--- a/lib/config/hadeployconfigverify_test.go
+++ b/lib/config/hadeployconfigverify_test.go
@@ -21,7 +21,7 @@ func TestParseAndVerify(t *testing.T) {
 			name:    "Verify AWS Config",
 			args:    args{configFile: "./testdata/HaAws.toml"},
 			wantErr: true,
-			err:     errors.New("invalid or empty: ssh_user\ninvalid or empty: ssh_key_file\ninvalid or empty s3_bucketName\ninvalid or empty: aws profile name\ninvalid or empty: aws region\ninvalid or empty: aws aws_vpc_id\ninvalid or empty: aws private_custom_subnets\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws opensearch_server_instance_type\ninvalid or empty: aws postgresql_server_instance_type\nempty value: aws opensearch_ebs_volume_iops\nempty value: aws opensearch_ebs_volume_size\ninvalid or empty: aws opensearch_ebs_volume_type\nempty value: aws postgresql_ebs_volume_iops\nempty value: aws postgresql_ebs_volume_size\ninvalid or empty: aws postgresql_ebs_volume_type\ninvalid or empty URL: automate fqdn\nempty value: automate instance_count\nempty value: chef server instance_count\nempty value: opensearch instance_count\nempty value: postgresql instance_count"),
+			err:     errors.New("invalid or empty: ssh_user\ninvalid or empty: ssh_key_file\ninvalid or empty s3_bucketName\ninvalid or empty: aws region\ninvalid or empty: aws aws_vpc_id\ninvalid or empty: aws private_custom_subnets\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws opensearch_server_instance_type\ninvalid or empty: aws postgresql_server_instance_type\nempty value: aws opensearch_ebs_volume_iops\nempty value: aws opensearch_ebs_volume_size\ninvalid or empty: aws opensearch_ebs_volume_type\nempty value: aws postgresql_ebs_volume_iops\nempty value: aws postgresql_ebs_volume_size\ninvalid or empty: aws postgresql_ebs_volume_type\ninvalid or empty URL: automate fqdn\nempty value: automate instance_count\nempty value: chef server instance_count\nempty value: opensearch instance_count\nempty value: postgresql instance_count"),
 		},
 		{
 			name:    "Verify AWS Managed Config",
@@ -51,13 +51,13 @@ func TestParseAndVerify(t *testing.T) {
 			name:    "Verify Aws Chef Managed Config",
 			args:    args{configFile: "./testdata/HaAwsChefManaged.toml"},
 			wantErr: true,
-			err:     errors.New("invalid value '1234567' for field 'ssh_port' port number must be between 1 and 65535\ninvalid or empty: ssh_key_file\ninvalid or empty: aws profile name\ninvalid or empty: aws region\ninvalid or empty: aws aws_vpc_id\nminimum number of aws private_custom_subnets required is 3\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws opensearch_server_instance_type\ninvalid or empty: aws postgresql_server_instance_type\nempty value: aws opensearch_ebs_volume_iops\nempty value: aws opensearch_ebs_volume_size\ninvalid or empty: aws opensearch_ebs_volume_type\nempty value: aws postgresql_ebs_volume_iops\nempty value: aws postgresql_ebs_volume_size\ninvalid or empty: aws postgresql_ebs_volume_type\ninvalid or empty URL: automate fqdn\npassword is too short (must be at least 8 characters)\nempty value: automate instance_count\nautomate public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\nempty value: chef server instance_count\nchefServer public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\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\nempty value: postgresql instance_count\npostgresql root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false"),
+			err:     errors.New("invalid value '1234567' for field 'ssh_port' port number must be between 1 and 65535\ninvalid or empty: ssh_key_file\ninvalid or empty: aws region\ninvalid or empty: aws aws_vpc_id\nminimum number of aws private_custom_subnets required is 3\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws opensearch_server_instance_type\ninvalid or empty: aws postgresql_server_instance_type\nempty value: aws opensearch_ebs_volume_iops\nempty value: aws opensearch_ebs_volume_size\ninvalid or empty: aws opensearch_ebs_volume_type\nempty value: aws postgresql_ebs_volume_iops\nempty value: aws postgresql_ebs_volume_size\ninvalid or empty: aws postgresql_ebs_volume_type\ninvalid or empty URL: automate fqdn\npassword is too short (must be at least 8 characters)\nempty value: automate instance_count\nautomate public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\nempty value: chef server instance_count\nchefServer public_key and/or private_key are missing. Otherwise set enable_custom_certs to false\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\nempty value: postgresql instance_count\npostgresql root_ca and/or public_key and/or private_key are missing. Otherwise set enable_custom_certs to false"),
 		},
 		{
 			name:    "Verify Failed Ha Aws Managed Config",
 			args:    args{configFile: "./testdata/HaAwsManagedFailed.toml"},
 			wantErr: true,
-			err:     errors.New("invalid or empty: secrets_key_file\ninvalid or empty: secrets_store_file\ninvalid value 'awss' for field 'architecture'. Expected values are: aws, existing_nodes, deployment\ninvalid value '/hab/a3_deploy_workspace' for field 'workspace_path'. Expected values are: /hab/a2_deploy_workspace\ninvalid or empty: ssh_user\ninvalid or empty: ssh_key_file\ninvalid or empty s3_bucketName\ninvalid or empty: aws profile name\ninvalid or empty: aws region\ninvalid or empty: aws private_custom_subnets\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid value 'aws lb_access_logs' for field 'fals'\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws managed_opensearch_domain_name\ninvalid or empty URL: aws managed_opensearch_domain_url\ninvalid or empty: aws managed_opensearch_user_password\ninvalid or empty: aws managed_opensearch_username\ninvalid or empty: aws managed_rds_dbuser_password\ninvalid or empty: aws managed_rds_dbuser_username\ninvalid or empty URL: aws managed_rds_instance_url\ninvalid or empty: aws managed_rds_superuser_password\ninvalid or empty: aws managed_rds_superuser_username\ninvalid or empty URL: automate fqdn\nempty value: automate instance_count\nempty value: chef server instance_count"),
+			err:     errors.New("invalid or empty: secrets_key_file\ninvalid or empty: secrets_store_file\ninvalid value 'awss' for field 'architecture'. Expected values are: aws, existing_nodes, deployment\ninvalid value '/hab/a3_deploy_workspace' for field 'workspace_path'. Expected values are: /hab/a2_deploy_workspace\ninvalid or empty: ssh_user\ninvalid or empty: ssh_key_file\ninvalid or empty s3_bucketName\ninvalid or empty: aws region\ninvalid or empty: aws private_custom_subnets\ninvalid or empty: aws public_custom_subnets\ninvalid or empty: aws ssh_key_pair_name\ninvalid value 'aws lb_access_logs' for field 'fals'\ninvalid or empty: aws ami_id\ninvalid or empty: aws automate_server_instance_type\ninvalid or empty: aws automate_lb_certificate_arn\nempty value: aws automate_ebs_volume_iops\nempty value: aws automate_ebs_volume_size\ninvalid or empty: aws automate_ebs_volume_type\nempty value: aws chef_ebs_volume_iops\nempty value: aws chef_ebs_volume_size\ninvalid or empty: aws chef_ebs_volume_type\ninvalid or empty: aws chef_server_instance_type\ninvalid or empty: aws chef_server_lb_certificate_arn\ninvalid or empty: aws managed_opensearch_domain_name\ninvalid or empty URL: aws managed_opensearch_domain_url\ninvalid or empty: aws managed_opensearch_user_password\ninvalid or empty: aws managed_opensearch_username\ninvalid or empty: aws managed_rds_dbuser_password\ninvalid or empty: aws managed_rds_dbuser_username\ninvalid or empty URL: aws managed_rds_instance_url\ninvalid or empty: aws managed_rds_superuser_password\ninvalid or empty: aws managed_rds_superuser_username\ninvalid or empty URL: automate fqdn\nempty value: automate instance_count\nempty value: chef server instance_count"),
 		},
 		{
 			name:    "Verify Failed Ha On Prem Aws Managed Config",
diff --git a/lib/httputils/http_test.go b/lib/httputils/http_test.go
index 92d2b8ccd..86dae82a0 100644
--- a/lib/httputils/http_test.go
+++ b/lib/httputils/http_test.go
@@ -95,3 +95,86 @@ func TestMakeRequestConnectionError(t *testing.T) {
 	assert.Nil(t, responseBody)
 	assert.Contains(t, err.Error(), "context deadline exceeded")
 }
+
+func TestMakeRequestWithHeaderSuccessfulRequest(t *testing.T) {
+	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		assert.Equal(t, http.MethodPost, r.Method)
+		assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
+
+		body, err := io.ReadAll(r.Body)
+		assert.NoError(t, err)
+
+		var requestBody map[string]interface{}
+		err = json.Unmarshal(body, &requestBody)
+		assert.NoError(t, err)
+
+		expectedBody := map[string]interface{}{
+			"key1": "value1",
+			"key2": "42",
+		}
+		assert.Equal(t, expectedBody, requestBody)
+
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte("Success"))
+	}))
+	defer server.Close()
+
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	client := httputils.NewClient(logger)
+
+	url := server.URL
+	requestBody := map[string]interface{}{
+		"key1": "value1",
+		"key2": "42",
+	}
+	resp, responseBody, err := client.MakeRequestWithHeaders(http.MethodPost, url, requestBody, "Content-Type", "application/json")
+
+	assert.NoError(t, err)
+	assert.NotNil(t, resp)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+
+	expectedResponseBody := "Success"
+	assert.Equal(t, expectedResponseBody, string(responseBody))
+}
+
+func TestMakeRequestWithHeaderConnectionError(t *testing.T) {
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+
+	client := httputils.NewClientWithTimeout(1*time.Millisecond, logger)
+
+	resp, responseBody, err := client.MakeRequestWithHeaders(http.MethodGet, "http://non-existent-server", nil, "Content-Type", "application/json")
+
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+	assert.Nil(t, responseBody)
+	assert.Contains(t, err.Error(), "context deadline exceeded")
+}
+
+func TestMakeRequestWithHeaderRequestError(t *testing.T) {
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	client := httputils.NewClient(logger)
+
+	resp, responseBody, err := client.MakeRequestWithHeaders(http.MethodGet, "invalid-url", nil, "Content-Type", "application/json")
+
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+	assert.Nil(t, responseBody)
+	assert.Contains(t, err.Error(), "failed to make HTTP request")
+}
+
+func TestMakeRequestWithHeaderRequestBodyError(t *testing.T) {
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	client := httputils.NewClient(logger)
+
+	resp, responseBody, err := client.MakeRequestWithHeaders(http.MethodPost, "https://example.com", make(chan int), "Content-Type", "application/json")
+
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+	assert.Nil(t, responseBody)
+	assert.Contains(t, err.Error(), "failed to marshal request body")
+}
diff --git a/lib/sshutils/sshutils_test.go b/lib/sshutils/sshutils_test.go
index ccb52cfca..cba3057a0 100644
--- a/lib/sshutils/sshutils_test.go
+++ b/lib/sshutils/sshutils_test.go
@@ -2,7 +2,9 @@ package sshutils_test
 
 import (
 	"errors"
+	"io/ioutil"
 	"net"
+	"path/filepath"
 
 	"testing"
 
@@ -652,7 +654,8 @@ func TestCheckKnownHosts(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			sshu.SshClient = tt.args.MockSshClient
-			_, gotError := sshu.CheckKnownHosts()
+			khdir := t.TempDir()
+			_, gotError := sshu.CheckKnownHosts(filepath.Join(khdir, sshutils.AUTOMATE_KNOWN_HOSTS))
 			assert.Equal(t, gotError, tt.wantedError)
 		})
 	}
@@ -672,7 +675,8 @@ func TestCreateKnownHosts(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
-			gotError := sshu.CreateKnownHosts()
+			khdir := t.TempDir()
+			gotError := sshu.CreateKnownHosts(filepath.Join(khdir, sshutils.AUTOMATE_KNOWN_HOSTS))
 			assert.Equal(t, gotError, tt.wantedError)
 		})
 	}
@@ -682,10 +686,12 @@ func TestAddHostKey(t *testing.T) {
 	log, _ := logger.NewLogger("text", "debug")
 	sshu := sshutils.NewSSHUtil(&sshutils.SshClient{}, log)
 	type args struct {
-		host          string
-		remote        *netAddressTest
-		pubkey        ssh.PublicKey
-		MockSshClient sshutils.ISshClient
+		host            string
+		remote          *netAddressTest
+		pubkey          ssh.PublicKey
+		MockSshClient   sshutils.ISshClient
+		existingContent string
+		expectedContent string
 	}
 	tests := []struct {
 		description string
@@ -693,19 +699,71 @@ func TestAddHostKey(t *testing.T) {
 		wantErr     error
 	}{
 		{
-			description: "If the Adding host key was successfully done",
+			description: "If the Adding host key was successfully done, empty known host file",
 			args: args{
 				host: nodeIp1,
 				remote: &netAddressTest{
 					Address: nodeIp1,
 				},
 				pubkey: &sshPublicKeyTest{
-					key:  "",
-					data: nil,
+					key:  "test-key",
+					data: []byte("test-data"),
 				},
 				MockSshClient: &sshutils.MockSshClient{
 					Normalizefunc: func(address string) string {
-						return ""
+						return nodeIp1
+					},
+					Newfunc: func(files ...string) (ssh.HostKeyCallback, error) {
+						return func(host string, remote net.Addr, pubkey ssh.PublicKey) error {
+							return nil
+						}, nil
+					},
+				},
+				existingContent: "",
+				expectedContent: nodeIp1 + " test-key dGVzdC1kYXRh",
+			},
+			wantErr: nil,
+		},
+		{
+			description: "If the Adding host key was successfully done, existing known host file",
+			args: args{
+				host: nodeIp1,
+				remote: &netAddressTest{
+					Address: nodeIp1,
+				},
+				pubkey: &sshPublicKeyTest{
+					key:  "test-key",
+					data: []byte("test-data"),
+				},
+				MockSshClient: &sshutils.MockSshClient{
+					Normalizefunc: func(address string) string {
+						return nodeIp1
+					},
+					Newfunc: func(files ...string) (ssh.HostKeyCallback, error) {
+						return func(host string, remote net.Addr, pubkey ssh.PublicKey) error {
+							return nil
+						}, nil
+					},
+				},
+				existingContent: nodeIp2 + " test-key dGVzdC1kYXRh",
+				expectedContent: nodeIp2 + " test-key dGVzdC1kYXRh\n" + nodeIp1 + " test-key dGVzdC1kYXRh",
+			},
+			wantErr: nil,
+		},
+		{
+			description: "If the Adding host key was successfully done, existing known host file with new line at end",
+			args: args{
+				host: nodeIp1,
+				remote: &netAddressTest{
+					Address: nodeIp1,
+				},
+				pubkey: &sshPublicKeyTest{
+					key:  "test-key",
+					data: []byte("test-data"),
+				},
+				MockSshClient: &sshutils.MockSshClient{
+					Normalizefunc: func(address string) string {
+						return nodeIp1
 					},
 					Newfunc: func(files ...string) (ssh.HostKeyCallback, error) {
 						return func(host string, remote net.Addr, pubkey ssh.PublicKey) error {
@@ -713,6 +771,8 @@ func TestAddHostKey(t *testing.T) {
 						}, nil
 					},
 				},
+				existingContent: nodeIp2 + " test-key dGVzdC1kYXRh\n",
+				expectedContent: nodeIp2 + " test-key dGVzdC1kYXRh\n" + nodeIp1 + " test-key dGVzdC1kYXRh",
 			},
 			wantErr: nil,
 		},
@@ -720,8 +780,18 @@ func TestAddHostKey(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			sshu.SshClient = tt.args.MockSshClient
-			gotError := sshu.AddHostKey(tt.args.host, tt.args.remote, tt.args.pubkey)
+			khdir := t.TempDir()
+			f, err := ioutil.TempFile(khdir, "")
+			assert.NoError(t, err)
+			content := []byte(tt.args.existingContent)
+			_, err = f.Write(content)
+			assert.NoError(t, err)
+			gotError := sshu.AddHostKey(tt.args.host, f.Name(), tt.args.remote, tt.args.pubkey)
 			assert.Equal(t, gotError, tt.wantErr)
+			content, err = ioutil.ReadFile(f.Name())
+			assert.NoError(t, err)
+			actualContent := string(content)
+			assert.Equal(t, tt.args.expectedContent, actualContent)
 		})
 	}
 }
diff --git a/lib/stringutils/strings_test.go b/lib/stringutils/strings_test.go
index cb5687a55..6ae82ec97 100644
--- a/lib/stringutils/strings_test.go
+++ b/lib/stringutils/strings_test.go
@@ -33,3 +33,23 @@ func TestTitleReplace(t *testing.T) {
 	assert.Equal(t, "Helloworld", stringutils.TitleReplace("helloworld", "-", " "))
 	assert.Equal(t, "Hello Chef", stringutils.TitleReplace("hello chef", "_", "-"))
 }
+
+func TestGetLastLine(t *testing.T) {
+	// Test case 1: Empty input string
+	input := ""
+	expected := ""
+	actual := stringutils.GetLastLine(input)
+	assert.Equal(t, actual, expected)
+
+	// Test case 2: Single line input
+	input = "Hello, World!"
+	expected = "Hello, World!"
+	actual = stringutils.GetLastLine(input)
+	assert.Equal(t, actual, expected)
+
+	// Test case 3: Multiple lines input
+	input = "Line 1\nLine 2\nLine 3\nLast line"
+	expected = "Last line"
+	actual = stringutils.GetLastLine(input)
+	assert.Equal(t, actual, expected)
+}