Repository: chef/automate
Pull request #7813 (2023-04-11 20:04:51)

Add missing config change for opensearch snapshot path
### :nut_and_bolt: Description: What code changed, and why?

Need to update the config path for the opensearch/elasticsearch snapshots to match the path from the backups. 

Otherwise, we'll get errors about being unable to create the appropriate snapshot repositories.

### :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


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


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 834885511b74aeb6eb4b3fa0dc810c989225f400 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/chef-automate/deploys/6435bd6671405f0008c6bc2a |
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=chef_automate&pullRequest=7813)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=chef_automate&pullRequest=7813&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=chef_automate&pullRequest=7813&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=chef_automate&pullRequest=7813&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=chef_automate&pullRequest=7813&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=chef_automate&pullRequest=7813&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=chef_automate&pullRequest=7813&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/.bldr.toml b/.bldr.toml
index c58bc3ae0..6818e031d 100644
--- a/.bldr.toml
+++ b/.bldr.toml
@@ -440,6 +440,7 @@ paths = [
   "components/local-user-service/*",
   "lib/cereal/*",
   "lib/config/*",
+  "lib/config_parser/*",
   "lib/db/*",
   "lib/grpc/*",
   "lib/httputils/*",
@@ -452,12 +453,14 @@ paths = [
   "lib/proc/*",
   "lib/product/*",
   "lib/proxy/*",
+  "lib/reporting/*",
   "lib/secrets/*",
   "lib/stringutils/*",
   "lib/tls/*",
   "lib/tracing/*",
   "lib/user/*",
   "lib/uuid4/*",
+  "lib/verification/*",
   "lib/version/*"
 ]
 
diff --git a/.license_scout.yml b/.license_scout.yml
index afb17194a..7c2c789b4 100644
--- a/.license_scout.yml
+++ b/.license_scout.yml
@@ -329,6 +329,9 @@ fallbacks:
     - name: github.com/apparentlymart/go-textseg
       license_id: Unlicense
       license_content: https://github.com/apparentlymart/go-textseg/blob/master/LICENSE
+    - name: github.com/globalsign/mgo
+      license_id: Unlicense
+      license_content: https://github.com/globalsign/mgo/blob/master/LICENSE
 
   habitat:
     - name: chef/mlsa
diff --git a/VERSION b/VERSION
index f2cd3d3d8..dec9ba5f3 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.5.105
\ No newline at end of file
+4.5.109
\ No newline at end of file
diff --git a/components/automate-cli/cmd/chef-automate/automateConstants.go b/components/automate-cli/cmd/chef-automate/automateConstants.go
index 1857cf080..4adc03cca 100644
--- a/components/automate-cli/cmd/chef-automate/automateConstants.go
+++ b/components/automate-cli/cmd/chef-automate/automateConstants.go
@@ -62,6 +62,10 @@ const (
 
 	GET_FRONTEND_CONFIG = `echo "y" | sudo chef-automate config show %s`
 
+	PRE_FLIGHT_CHECK = `cd /tmp;
+	chmod +x chef-automate;
+	sudo ./chef-automate preflight-check --airgap`
+
 	GET_APPLIED_CONFIG = `
 	source <(sudo cat /hab/sup/default/SystemdEnvironmentFile.sh);
 	automate-backend-ctl applied --svc=automate-ha-%s
diff --git a/components/automate-cli/cmd/chef-automate/cmdUtils.go b/components/automate-cli/cmd/chef-automate/cmdUtils.go
index 1c69b9802..7912cf4ea 100644
--- a/components/automate-cli/cmd/chef-automate/cmdUtils.go
+++ b/components/automate-cli/cmd/chef-automate/cmdUtils.go
@@ -128,6 +128,11 @@ func (c *remoteCmdExecutor) executeCmdOnGivenNodes(input *CmdInputs, nodeIps []s
 	inputFileToOutputFileMap := map[string]string{}
 	for _, file := range inputFiles {
 		destinationFile := remoteService + "_" + timestamp + "_" + file
+		if strings.Contains(file, "/") {
+			filePath := strings.Split(file, "/")
+			lastFileidx := len(filePath) - 1
+			destinationFile = filePath[lastFileidx]
+		}
 		inputFileToOutputFileMap[file] = destinationFile
 	}
 
@@ -185,7 +190,7 @@ func (c *remoteCmdExecutor) executeCmdOnNode(command string, inputFiles map[stri
 	}
 
 	output, err := sshUtil.connectAndExecuteCommandOnRemote(command, true)
-	if err != nil {
+	if err != nil && len(output) == 0 {
 		rc.Error = err
 		resultChan <- rc
 		return
@@ -205,9 +210,8 @@ func (c *remoteCmdExecutor) executeCmdOnNode(command string, inputFiles map[stri
 		}
 	}
 
-	if errorCheckEnableInOutput {
-		err = checkResultOutputForError(output)
-	}
+	err = checkIfErrorPresentInOutput(errorCheckEnableInOutput, output)
+
 	if err != nil {
 		rc.Error = err
 		resultChan <- rc
@@ -218,6 +222,13 @@ func (c *remoteCmdExecutor) executeCmdOnNode(command string, inputFiles map[stri
 	resultChan <- rc
 }
 
+func checkIfErrorPresentInOutput(errorCheckEnableInOutput bool, output string) error {
+	if errorCheckEnableInOutput {
+		return checkResultOutputForError(output)
+	}
+	return nil
+}
+
 // preCmdExecCheck will check and execute PreExec function. Also returns nodeips for given remoteservice with error if any.
 func preCmdExecCheck(node *Cmd, sshUtil SSHUtil, infra *AutomteHAInfraDetails, remoteService string, timestamp string, writer *cli.Writer) ([]string, error) {
 	var nodeIps []string
@@ -382,7 +393,9 @@ func appendChildFileToParentFile(hostIp, parent, child string) (string, error) {
 
 // checkResultOutputForError checks If the output contains the word "error" then return error
 func checkResultOutputForError(output string) error {
-	if strings.Contains(strings.ToUpper(strings.TrimSpace(output)), "ERROR") {
+	if strings.Contains(strings.ToUpper(strings.TrimSpace(output)), "ERROR") &&
+		!strings.Contains(strings.ToUpper(strings.TrimSpace(output)),
+			strings.ToUpper("PreflightError: One or more preflight checks failed")) {
 		return errors.New(output)
 	}
 	return nil
diff --git a/components/automate-cli/cmd/chef-automate/pre_flight_check.go b/components/automate-cli/cmd/chef-automate/pre_flight_check.go
index b885c5264..41d8b4172 100644
--- a/components/automate-cli/cmd/chef-automate/pre_flight_check.go
+++ b/components/automate-cli/cmd/chef-automate/pre_flight_check.go
@@ -7,6 +7,7 @@ import (
 	"os"
 	"strings"
 
+	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	"github.com/chef/automate/api/config/deployment"
@@ -17,12 +18,84 @@ import (
 	"github.com/chef/automate/lib/version"
 )
 
+var preflightCmdFlags = struct {
+	airgap             bool
+	waitTimeout        int
+	configPath         string
+	haDeploymentConfig bool
+	automate           bool
+	chef_server        bool
+	frontend           bool
+	node               string
+}{}
+
+type InfraKeyValues struct {
+	Value []string `json:"value"`
+	Type  []string `json:"type"`
+}
+
+type InfraKeyValue struct {
+	Value string `json:"value"`
+	Type  string `json:"type"`
+}
+
 func init() {
 	preflightCheckCmd.PersistentFlags().BoolVar(
 		&preflightCmdFlags.airgap,
 		"airgap",
 		false,
 		"Pass this flag when the environment is airgapped")
+	preflightCheckCmd.PersistentFlags().BoolVar(
+		&preflightCmdFlags.haDeploymentConfig,
+		"ha-deployment-config",
+		false,
+		"Pass this flag to run pre-flight check on automate-HA")
+	showConfigCmd.PersistentFlags().SetAnnotation("ha-deployment-config", docs.Compatibility, []string{docs.CompatiblewithHA})
+	preflightCheckCmd.PersistentFlags().BoolVar(
+		&preflightCmdFlags.frontend,
+		"fe",
+		false,
+		"Pass this flag to run pre-flight check on all Frontend nodes")
+	showConfigCmd.PersistentFlags().SetAnnotation("fe", docs.Compatibility, []string{docs.CompatiblewithHA})
+	preflightCheckCmd.PersistentFlags().BoolVarP(
+		&preflightCmdFlags.frontend,
+		"frontend",
+		"f",
+		false,
+		"Pass this flag to run pre-flight check on all Frontend nodes")
+	showConfigCmd.PersistentFlags().SetAnnotation("frontend", docs.Compatibility, []string{docs.CompatiblewithHA})
+	preflightCheckCmd.PersistentFlags().BoolVarP(
+		&preflightCmdFlags.automate,
+		"automate",
+		"a",
+		false,
+		"Pass this flag to run pre-flight check on automate node(HA)")
+	showConfigCmd.PersistentFlags().SetAnnotation("automate", docs.Compatibility, []string{docs.CompatiblewithHA})
+	preflightCheckCmd.PersistentFlags().BoolVar(
+		&preflightCmdFlags.automate,
+		"a2",
+		false,
+		"Pass this flag to run pre-flight check on automate node(HA)")
+	showConfigCmd.PersistentFlags().SetAnnotation("a2", docs.Compatibility, []string{docs.CompatiblewithHA})
+	preflightCheckCmd.PersistentFlags().BoolVarP(
+		&preflightCmdFlags.chef_server,
+		"chef_server",
+		"c",
+		false,
+		"Pass this flag to run pre-flight check on chef-server node(HA)")
+	showConfigCmd.PersistentFlags().SetAnnotation("chef_server", docs.Compatibility, []string{docs.CompatiblewithHA})
+	preflightCheckCmd.PersistentFlags().BoolVar(
+		&preflightCmdFlags.chef_server,
+		"cs",
+		false,
+		"Pass this flag to run pre-flight check on chef-server node(HA)")
+	showConfigCmd.PersistentFlags().SetAnnotation("cs", docs.Compatibility, []string{docs.CompatiblewithHA})
+	preflightCheckCmd.PersistentFlags().StringVar(
+		&preflightCmdFlags.node,
+		"node",
+		"",
+		"Pass this flag to run pre-flight check on a perticular node node(HA)")
+	showConfigCmd.PersistentFlags().SetAnnotation("node", docs.Compatibility, []string{docs.CompatiblewithHA})
 	preflightCheckCmd.PersistentFlags().StringVar(
 		&preflightCmdFlags.configPath,
 		"config",
@@ -33,11 +106,6 @@ func init() {
 	preflightCheckCmd.AddCommand(newMigratePreflightCmd())
 }
 
-var preflightCmdFlags = struct {
-	airgap     bool
-	configPath string
-}{}
-
 var preflightCheckCmd = &cobra.Command{
 	Use:   "preflight-check",
 	Short: "Perform preflight check",
@@ -58,6 +126,26 @@ func loadMergedConfigForPreflight() (*deployment.AutomateConfig, error) {
 }
 
 func runPreflightCheckCmd(cmd *cobra.Command, args []string) error {
+	if preflightCmdFlags.haDeploymentConfig {
+		var configPath = ""
+		if len(args) == 0 {
+			return errors.Errorf("Config file should be passed with ha-deployment-config flag")
+		}
+		configPath = args[0]
+		infra, err := getInfraDetails(configPath)
+		if err != nil {
+			return err
+		}
+		nodeMap := constructAndGetNodeMap(infra)
+		cmdUtil := NewRemoteCmdExecutor(nodeMap)
+
+		if preflightCmdFlags.automate || preflightCmdFlags.chef_server || preflightCmdFlags.frontend {
+			err = cmdUtil.Execute()
+		} else {
+			writer.Println(cmd.UsageString())
+		}
+		return err
+	}
 	cfg, err := loadMergedConfigForPreflight()
 	if err != nil {
 		return err
@@ -65,10 +153,28 @@ func runPreflightCheckCmd(cmd *cobra.Command, args []string) error {
 	if err := client.Preflight(writer, cfg, version.BuildTime, preflightCmdFlags.airgap); err != nil {
 		return err
 	}
-
 	return nil
 }
 
+func getInfraDetails(configPath string) (*AutomteHAInfraDetails, error) {
+	var infra *AutomteHAInfraDetails
+	var err error
+	var deploymentType string
+	if isA2HARBFileExist() {
+		infra, err = getAutomateHAInfraDetails()
+	} else {
+		deploymentType, err = getModeFromConfig(configPath)
+		if err != nil {
+			return nil, errors.Errorf("%v", err)
+		}
+		infra, err = getInfraDetailsForDeploymentType(deploymentType, configPath)
+		if err != nil {
+			return nil, errors.Errorf("%v", err)
+		}
+	}
+	return infra, err
+}
+
 var migratePreflightCmdFlags = migrateCmdFlagSet{}
 
 func newMigratePreflightCmd() *cobra.Command {
@@ -344,3 +450,89 @@ func runChefServerChecks(u *a1upgrade.A1Upgrade) error {
 
 	return nil
 }
+
+func populateInfraDetails(config ExistingInfraConfigToml) *AutomteHAInfraDetails {
+
+	var infra AutomteHAInfraDetails
+	infra.Outputs.SSHKeyFile = InfraKeyValue{
+		Value: config.Architecture.ConfigInitials.SSHKeyFile,
+	}
+	infra.Outputs.SSHPort = InfraKeyValue{
+		Value: config.Architecture.ConfigInitials.SSHPort,
+	}
+	infra.Outputs.SSHUser = InfraKeyValue{
+		Value: config.Architecture.ConfigInitials.SSHUser,
+	}
+	infra.Outputs.AutomatePrivateIps = InfraKeyValues{
+		Value: config.ExistingInfra.Config.AutomatePrivateIps,
+	}
+	infra.Outputs.ChefServerPrivateIps = InfraKeyValues{
+		Value: config.ExistingInfra.Config.ChefServerPrivateIps,
+	}
+	return &infra
+}
+
+func getInfraDetailsForDeploymentType(deploymentType string, configPath string) (*AutomteHAInfraDetails, error) {
+	var infra *AutomteHAInfraDetails
+	var err error
+	if deploymentType == EXISTING_INFRA_MODE {
+		config, err := getExistingInfraConfig(configPath)
+		if err != nil {
+			return nil, errors.Errorf("%v", err)
+		}
+		infra = populateInfraDetails(*config)
+	}
+
+	if deploymentType == AWS_MODE {
+		infra, err = getAutomateHAInfraDetails()
+		if err != nil {
+			return nil, errors.Errorf("%v", err)
+		}
+	}
+	return infra, nil
+}
+
+func constructAndGetNodeMap(infra *AutomteHAInfraDetails) *NodeTypeAndCmd {
+	frontendCommand := fmt.Sprint(PRE_FLIGHT_CHECK)
+	inputFilePath := "/usr/bin/chef-automate"
+	nodeMap := &NodeTypeAndCmd{
+		Frontend: &Cmd{
+			CmdInputs: &CmdInputs{
+				Cmd:                      frontendCommand,
+				WaitTimeout:              preflightCmdFlags.waitTimeout,
+				ErrorCheckEnableInOutput: true,
+				Single:                   false,
+				NodeIp:                   preflightCmdFlags.node,
+				InputFiles:               []string{inputFilePath},
+				Outputfiles:              []string{},
+				NodeType:                 preflightCmdFlags.frontend,
+			},
+		},
+		Automate: &Cmd{
+			CmdInputs: &CmdInputs{
+				Cmd:                      frontendCommand,
+				WaitTimeout:              preflightCmdFlags.waitTimeout,
+				ErrorCheckEnableInOutput: true,
+				Single:                   false,
+				NodeIp:                   preflightCmdFlags.node,
+				InputFiles:               []string{inputFilePath},
+				Outputfiles:              []string{},
+				NodeType:                 preflightCmdFlags.automate,
+			},
+		},
+		ChefServer: &Cmd{
+			CmdInputs: &CmdInputs{
+				Cmd:                      frontendCommand,
+				WaitTimeout:              preflightCmdFlags.waitTimeout,
+				ErrorCheckEnableInOutput: true,
+				Single:                   false,
+				NodeIp:                   preflightCmdFlags.node,
+				InputFiles:               []string{inputFilePath},
+				Outputfiles:              []string{},
+				NodeType:                 preflightCmdFlags.chef_server,
+			},
+		},
+		Infra: infra,
+	}
+	return nodeMap
+}
diff --git a/components/automate-cli/cmd/chef-automate/sshUtils.go b/components/automate-cli/cmd/chef-automate/sshUtils.go
index 11dba7e23..7a5b7baa2 100644
--- a/components/automate-cli/cmd/chef-automate/sshUtils.go
+++ b/components/automate-cli/cmd/chef-automate/sshUtils.go
@@ -299,7 +299,10 @@ func (s *SSHUtilImpl) copyFileToRemote(srcFilePath string, destFileName string,
 	cmd := "scp"
 	exec_args := []string{"-P " + s.SshConfig.sshPort, "-o StrictHostKeyChecking=no", "-i", s.SshConfig.sshKeyFile, "-r", srcFilePath, s.SshConfig.sshUser + "@" + s.SshConfig.hostIP + ":/tmp/" + destFileName}
 	if err := exec.Command(cmd, exec_args...).Run(); err != nil {
-		writer.Printf("Failed to copy config file to remote %s\n", err.Error())
+		writer.Printf("\n"+"Failed to copy file %s to remote %s\n", srcFilePath, err.Error())
+		if srcFilePath == "/usr/bin/chef-automate" {
+			writer.Printf("Please copy your chef-automate binary to /usr/bin" + "\n")
+		}
 		return err
 	}
 	if removeFile {
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
new file mode 100644
index 000000000..56e3c9f83
--- /dev/null
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -0,0 +1,223 @@
+package main
+
+import (
+	"fmt"
+
+	"github.com/chef/automate/components/automate-cli/pkg/docs"
+	"github.com/chef/automate/components/automate-cli/pkg/status"
+	verification "github.com/chef/automate/lib/verification"
+	"github.com/spf13/cobra"
+)
+
+type verifyCmdFlags struct {
+	file                      string
+	haAWSProvision            bool
+	haAWSManagedProvision     bool
+	haOnpremDeploy            bool
+	haOnPremAWSManagedDeploy  bool
+	haOnPremCustManagedDeploy bool
+	haAWSDeploy               bool
+	haAWSManagedDeploy        bool
+	standaloneDeploy          bool
+	certificates              bool
+}
+
+type verifyCmdFlow struct {
+	Verification      verification.Verification
+	A2HARBFileExist   bool
+	ManagedServicesOn bool
+}
+
+func init() {
+	flagsObj := verifyCmdFlags{}
+
+	verifyCmd := &cobra.Command{
+		Use:   "verify",
+		Short: "Verify Chef Automate setup",
+		Long:  "Verify Chef Automate config files and Infrastructure",
+		Annotations: map[string]string{
+			docs.Compatibility: docs.Compatibility,
+		},
+		Args: cobra.RangeArgs(0, 1),
+		RunE: verifyCmdFunc(&flagsObj),
+	}
+
+	// flags for Verify Command
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.haAWSProvision,
+		"ha-aws-provision",
+		false,
+		"Use this flag to verify the AWS Provision config")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.haAWSManagedProvision,
+		"ha-aws-managed-provision",
+		false,
+		"Use this flag to verify the AWS Provision config with Managed services")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.haOnpremDeploy,
+		"ha-onprem-deploy",
+		false,
+		"Use this flag to verify the On-Premise setup and config with Chef Managed services")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.haOnPremAWSManagedDeploy,
+		"ha-onprem-aws-deploy",
+		false,
+		"Use this flag to verify the On-Premise setup and config with AWS Managed services")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.haOnPremCustManagedDeploy,
+		"ha-onprem-customer-deploy",
+		false,
+		"Use this flag to verify the On-Premise setup and config with Customer Managed services")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.haAWSDeploy,
+		"ha-aws-deploy",
+		false,
+		"Use this flag to verify the AWS Deployment setup and config")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.haAWSManagedDeploy,
+		"ha-aws-managed-deploy",
+		false,
+		"Use this flag to verify the AWS Deployment setup and config with Managed Services")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.standaloneDeploy,
+		"standalone-deploy",
+		false,
+		"Use this flag to verify the Automate standalone setup and config")
+	verifyCmd.PersistentFlags().BoolVar(
+		&flagsObj.certificates,
+		"certificates",
+		false,
+		"Use this flag to verify the certificates provided in the file")
+	verifyCmd.PersistentFlags().StringVar(
+		&flagsObj.file,
+		"file",
+		"",
+		"Config file that needs to be verified")
+	RootCmd.AddCommand(verifyCmd)
+}
+
+func verifyCmdFunc(flagsObj *verifyCmdFlags) func(cmd *cobra.Command, args []string) error {
+	return func(cmd *cobra.Command, args []string) error {
+		c := verifyCmdFlow{
+			Verification:      &verification.VerificationModule{},
+			A2HARBFileExist:   isA2HARBFileExist(),
+			ManagedServicesOn: isManagedServicesOn(),
+		}
+		return c.runVerifyCmd(cmd, args, flagsObj)
+	}
+}
+
+func (v *verifyCmdFlow) runVerifyCmd(cmd *cobra.Command, args []string, flagsObj *verifyCmdFlags) error {
+	var configPath = ""
+	if len(args) > 0 {
+		configPath = args[0]
+	}
+
+	switch true {
+	case flagsObj.haAWSProvision:
+		err := v.verifyHaAWSProvision(configPath)
+		return err
+	case flagsObj.haAWSManagedProvision:
+		err := v.verifyHaAWSManagedProvision(configPath)
+		return err
+	case flagsObj.haAWSDeploy:
+		err := v.verifyHaAWSDeploy(configPath)
+		return err
+	case flagsObj.haAWSManagedDeploy:
+		err := v.verifyHaAWSManagedDeploy(configPath)
+		return err
+	case flagsObj.standaloneDeploy:
+		err := v.verifyStandaloneDeploy(configPath)
+		return err
+	case flagsObj.haOnpremDeploy:
+		err := v.verifyHaOnpremDeploy(configPath)
+		return err
+	case flagsObj.haOnPremAWSManagedDeploy:
+		err := v.verifyHaOnPremAWSManagedDeploy(configPath)
+		return err
+	case flagsObj.haOnPremCustManagedDeploy:
+		err := v.verifyHaOnPremCustManagedDeploy(configPath)
+		return err
+	case flagsObj.certificates:
+		err := v.Verification.VerifyCertificates("")
+		return err
+	default:
+		fmt.Println(cmd.UsageString())
+		return nil
+	}
+
+}
+
+func (v *verifyCmdFlow) verifyHaAWSProvision(configPath string) error {
+	if err := v.Verification.VerifyHAAWSProvision(configPath); err != nil {
+		return status.Annotate(err, status.ConfigError)
+	}
+
+	return nil
+}
+
+func (v *verifyCmdFlow) verifyHaAWSManagedProvision(configPath string) error {
+	if err := v.Verification.VerifyHAAWSManagedProvision(configPath); err != nil {
+		return status.Annotate(err, status.ConfigError)
+	}
+
+	return nil
+}
+
+func (v *verifyCmdFlow) verifyHaAWSDeploy(configPath string) error {
+	if !v.ManagedServicesOn {
+		if v.A2HARBFileExist {
+			if err := v.Verification.VerifyHAAWSDeployment(configPath); err != nil {
+				return status.Annotate(err, status.ConfigError)
+			}
+			return nil
+		}
+		return status.New(status.InvalidCommandArgsError, errProvisonInfra)
+	}
+	return status.New(status.InvalidCommandArgsError, "This flag will not verify the Managed Services Setup. Please use the --ha-aws-managed-deploy flag.")
+}
+
+func (v *verifyCmdFlow) verifyHaAWSManagedDeploy(configPath string) error {
+	if v.ManagedServicesOn {
+		if v.A2HARBFileExist {
+			if err := v.Verification.VerifyHAAWSManagedDeployment(configPath); err != nil {
+				return status.Annotate(err, status.ConfigError)
+			}
+			return nil
+		}
+		return status.New(status.InvalidCommandArgsError, errProvisonInfra)
+	}
+	return status.New(status.InvalidCommandArgsError, "Managed Services flag is not set. Cannot verify the config.")
+}
+
+func (v *verifyCmdFlow) verifyStandaloneDeploy(configPath string) error {
+	if v.A2HARBFileExist {
+		return status.New(status.InvalidCommandArgsError, "Deployment type does not match with the requested flag.")
+	}
+	if err := v.Verification.VerifyStandaloneDeployment(configPath); err != nil {
+		return status.Annotate(err, status.ConfigError)
+	}
+
+	return nil
+}
+
+func (v *verifyCmdFlow) verifyHaOnpremDeploy(configPath string) error {
+	if err := v.Verification.VerifyOnPremDeployment(configPath); err != nil {
+		return status.Annotate(err, status.ConfigError)
+	}
+	return nil
+}
+
+func (v *verifyCmdFlow) verifyHaOnPremAWSManagedDeploy(configPath string) error {
+	if err := v.Verification.VerifyOnPremAWSManagedDeployment(configPath); err != nil {
+		return status.Annotate(err, status.ConfigError)
+	}
+	return nil
+}
+
+func (v *verifyCmdFlow) verifyHaOnPremCustManagedDeploy(configPath string) error {
+	if err := v.Verification.VerifyOnPremCustManagedDeployment(configPath); err != nil {
+		return status.Annotate(err, status.ConfigError)
+	}
+	return nil
+}
diff --git a/components/docs-chef-io/content/automate/ha_automate_to_automate_ha.md b/components/docs-chef-io/content/automate/ha_automate_to_automate_ha.md
index b2b1d718a..ca2d84999 100644
--- a/components/docs-chef-io/content/automate/ha_automate_to_automate_ha.md
+++ b/components/docs-chef-io/content/automate/ha_automate_to_automate_ha.md
@@ -78,6 +78,13 @@ minimum version of Chef Automate: [20201230192246](https://docs.chef.io/release_
     {{% automate/char-warn %}}
     {{< /warning >}}
 
+    Update the backup paths to match the elasticsearch snapshot location from the A2HA:
+
+    ```bash
+    [global.v1.external.opensearch.backup.fs]
+    path = "/mnt/automate_backups/elasticsearch"
+    ```
+
 1. Unpack the `bootstrap.abb` file on all the Frontend nodes:
 
     Login to Each Frontend Node and then run after copying the `bootstrap.abb` file.
diff --git a/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate.yaml b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate.yaml
index 0d79e21a9..2e32cfa43 100644
--- a/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate.yaml
+++ b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate.yaml
@@ -55,6 +55,7 @@ see_also:
 - test - Run Automate HA smoke tests
 - uninstall - Uninstall Chef Automate
 - upgrade - upgrade automate to the latest version
+- verify - Verify Chef Automate setup
 - version - Show CLI version
 - workspace - Set workspace env for Automate HA.
 supported_on: Bastion
diff --git a/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check.yaml b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check.yaml
index cd59def0e..7291cad28 100644
--- a/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check.yaml
+++ b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check.yaml
@@ -4,15 +4,44 @@ usage: chef-automate preflight-check [flags]
 description: |
   Perform preflight check to verify host meets installation criteria.
 options:
+- name: a2
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on automate node(HA)
 - name: airgap
   default_value: "false"
   usage: Pass this flag when the environment is airgapped
+- name: automate
+  shorthand: a
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on automate node(HA)
+- name: chef_server
+  shorthand: c
+  default_value: "false"
+  usage: |
+    Pass this flag to run pre-flight check on chef-server node(HA)
 - name: config
   usage: Optional config file to use
+- name: cs
+  default_value: "false"
+  usage: |
+    Pass this flag to run pre-flight check on chef-server node(HA)
+- name: fe
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on all Frontend nodes
+- name: frontend
+  shorthand: f
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on all Frontend nodes
+- name: ha-deployment-config
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on automate-HA
 - name: help
   shorthand: h
   default_value: "false"
   usage: help for preflight-check
+- name: node
+  usage: |
+    Pass this flag to run pre-flight check on a perticular node node(HA)
 inherited_options:
 - name: debug
   shorthand: d
diff --git a/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check_migrate-from-v1.yaml b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check_migrate-from-v1.yaml
index 6280bc62d..b6e9327a6 100644
--- a/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check_migrate-from-v1.yaml
+++ b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_preflight-check_migrate-from-v1.yaml
@@ -40,18 +40,47 @@ options:
   usage: |
     Optionally do not check if your Chef Automate v1 installation has workflow configured (default = false)
 inherited_options:
+- name: a2
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on automate node(HA)
 - name: airgap
   default_value: "false"
   usage: Pass this flag when the environment is airgapped
+- name: automate
+  shorthand: a
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on automate node(HA)
+- name: chef_server
+  shorthand: c
+  default_value: "false"
+  usage: |
+    Pass this flag to run pre-flight check on chef-server node(HA)
 - name: config
   usage: Optional config file to use
+- name: cs
+  default_value: "false"
+  usage: |
+    Pass this flag to run pre-flight check on chef-server node(HA)
 - name: debug
   shorthand: d
   default_value: "false"
   usage: Enable debug output
+- name: fe
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on all Frontend nodes
+- name: frontend
+  shorthand: f
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on all Frontend nodes
+- name: ha-deployment-config
+  default_value: "false"
+  usage: Pass this flag to run pre-flight check on automate-HA
 - name: no-check-version
   default_value: "false"
   usage: Disable version check
+- name: node
+  usage: |
+    Pass this flag to run pre-flight check on a perticular node node(HA)
 - name: result-json
   usage: Write command result as JSON to PATH
 see_also:
diff --git a/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_verify.yaml b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_verify.yaml
new file mode 100644
index 000000000..5cba26356
--- /dev/null
+++ b/components/docs-chef-io/data/automate/cli_chef_automate/commands/chef-automate_verify.yaml
@@ -0,0 +1,58 @@
+name: chef-automate verify
+synopsis: Verify Chef Automate setup
+usage: chef-automate verify [flags]
+description: Verify Chef Automate config files and Infrastructure
+options:
+- name: certificates
+  default_value: "false"
+  usage: |
+    Use this flag to verify the certificates provided in the file
+- name: file
+  usage: Config file that needs to be verified
+- name: ha-aws-deploy
+  default_value: "false"
+  usage: Use this flag to verify the AWS Deployment setup and config
+- name: ha-aws-managed-deploy
+  default_value: "false"
+  usage: |
+    Use this flag to verify the AWS Deployment setup and config with Managed Services
+- name: ha-aws-managed-provision
+  default_value: "false"
+  usage: |
+    Use this flag to verify the AWS Provision config with Managed services
+- name: ha-aws-provision
+  default_value: "false"
+  usage: Use this flag to verify the AWS Provision config
+- name: ha-onprem-aws-deploy
+  default_value: "false"
+  usage: |
+    Use this flag to verify the On-Premise setup and config with AWS Managed services
+- name: ha-onprem-customer-deploy
+  default_value: "false"
+  usage: |
+    Use this flag to verify the On-Premise setup and config with Customer Managed services
+- name: ha-onprem-deploy
+  default_value: "false"
+  usage: |
+    Use this flag to verify the On-Premise setup and config with Chef Managed services
+- name: help
+  shorthand: h
+  default_value: "false"
+  usage: help for verify
+- name: standalone-deploy
+  default_value: "false"
+  usage: |
+    Use this flag to verify the Automate standalone setup and config
+inherited_options:
+- name: debug
+  shorthand: d
+  default_value: "false"
+  usage: Enable debug output
+- name: no-check-version
+  default_value: "false"
+  usage: Disable version check
+- name: result-json
+  usage: Write command result as JSON to PATH
+see_also:
+- chef-automate - Chef Automate CLI
+compatible_with: compatibility
diff --git a/go.mod b/go.mod
index c553dcab3..706b701d0 100644
--- a/go.mod
+++ b/go.mod
@@ -67,6 +67,7 @@ require (
 	github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365 // indirect
 	github.com/imdario/mergo v0.3.13
 	github.com/jaswdr/faker v1.0.2
+	github.com/jedib0t/go-pretty/v5 v5.0.2-alpha
 	github.com/jhump/protoreflect v1.7.1-0.20200723220026-11eaaf73e0ec
 	github.com/kevinburke/go-bindata v3.16.0+incompatible
 	github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348
@@ -74,7 +75,6 @@ require (
 	github.com/lib/pq v1.8.0
 	github.com/lyft/protoc-gen-star v0.4.16-0.20200805193024-077ca8f98fb2
 	github.com/mattn/go-isatty v0.0.12 // indirect
-	github.com/mattn/go-runewidth v0.0.9 // indirect
 	github.com/mattn/go-sqlite3 v1.11.0 // indirect
 	github.com/minio/minio-go/v7 v7.0.14
 	github.com/mitchellh/go-homedir v1.1.0
diff --git a/go.sum b/go.sum
index aba2a3d83..a71edd28b 100644
--- a/go.sum
+++ b/go.sum
@@ -122,6 +122,8 @@ github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQh
 github.com/armon/go-metrics v0.3.2 h1:EyUnxyP2yaGpLgMiuyyz8sHnByqeTJUfGs72pdH0i4A=
 github.com/armon/go-metrics v0.3.2/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco=
+github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
 github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/aws/aws-sdk-go v1.19.45/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@@ -236,6 +238,9 @@ github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2H
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.1-0.20180503022059-e9ed3c6dfb39 h1:8aiwjJixH9Dws9c0mE6yeghmrVhe6csuB+onSjeW31Q=
 github.com/ghodss/yaml v1.0.1-0.20180503022059-e9ed3c6dfb39/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
+github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/go-chef/chef v0.23.2-0.20201213054559-3243f736d651 h1:r1JMgPxoXlMKblknr8f6f3SafrfL2oKyPCqglTQN7xA=
 github.com/go-chef/chef v0.23.2-0.20201213054559-3243f736d651/go.mod h1:9Ptf9dFZDkpC3qLxpNhHMyG6TxCmMzonqPKXTh+xVm4=
 github.com/go-delve/delve v1.3.1 h1:LI0X4aAE0N7ay8uR48aMJ6GNTxamIpcxi1THjwJjenI=
@@ -253,6 +258,11 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/errors v0.19.0 h1:guf3T2lnCBKlODmERt4T9GtMWRpJOikgKGyIvi0xcb8=
+github.com/go-openapi/errors v0.19.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
+github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk=
+github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -444,6 +454,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jaswdr/faker v1.0.2 h1:3iRZztCC4oU67i67qGcyhIxeKgHQQH/iCifJOj3uOSI=
 github.com/jaswdr/faker v1.0.2/go.mod h1:9S4x1SRPC3m+iLgZTx2HZ0/R1/B0hwuL8uvOAiuJNtg=
+github.com/jedib0t/go-pretty/v5 v5.0.2-alpha h1:rRYHSFWZ3egaMVREb5NgN17UanLMWM2s7EhG3u/1Yy4=
+github.com/jedib0t/go-pretty/v5 v5.0.2-alpha/go.mod h1:DAKhNY3KVw+n6uMJ+WIzEs2XHfuQpMu2taSsKOXbTBw=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jhump/protoreflect v1.7.1-0.20200723220026-11eaaf73e0ec h1:LeWD9kGWul9js18sWzNP2SjMuO6smUEpXt8OmRs7qU0=
 github.com/jhump/protoreflect v1.7.1-0.20200723220026-11eaaf73e0ec/go.mod h1:D8nEtk+lKr8kMTMiLayswAtSSeHYDaRYk9r78Cy11mM=
@@ -513,6 +525,8 @@ github.com/lyft/protoc-gen-star v0.4.16-0.20200805193024-077ca8f98fb2 h1:P5EAf1f
 github.com/lyft/protoc-gen-star v0.4.16-0.20200805193024-077ca8f98fb2/go.mod h1:mE8fbna26u7aEA2QCVvvfBU/ZrPgocG1206xAFPcs94=
 github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@@ -628,6 +642,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
 github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
@@ -913,6 +928,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20171017063910-8dbc5d05d6ed/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
diff --git a/lib/config_parser/parser.go b/lib/config_parser/parser.go
new file mode 100644
index 000000000..e37dc0c80
--- /dev/null
+++ b/lib/config_parser/parser.go
@@ -0,0 +1,78 @@
+package config_parser
+
+import (
+	sc "github.com/chef/automate/api/config/deployment"
+	"github.com/chef/automate/components/automate-cli/pkg/status"
+	"github.com/chef/automate/lib/io/fileutils"
+	ptoml "github.com/pelletier/go-toml"
+)
+
+type ConfigParser interface {
+	ParseAWSAutomateConfig(configFile string) (*HAAwsConfigToml, error)
+	ParseOnPremConfig(configFile string) (*HAOnPremConfigToml, error)
+	ParseStandaloneConfig(configFile string) (*sc.AutomateConfig, error)
+}
+
+type ConfigParserImpl struct{}
+
+func (cp *ConfigParserImpl) ParseAWSAutomateConfig(configFile string) (*HAAwsConfigToml, error) {
+	return ParseAWSAutomateConfig(configFile)
+}
+
+func (cp *ConfigParserImpl) ParseOnPremConfig(configFile string) (*HAOnPremConfigToml, error) {
+	return ParseOnPremConfig(configFile)
+}
+
+func (cp *ConfigParserImpl) ParseStandaloneConfig(configFile string) (*sc.AutomateConfig, error) {
+	return ParseStandaloneConfig(configFile)
+}
+
+func ParseAWSAutomateConfig(configFile string) (*HAAwsConfigToml, error) {
+
+	/* This function will read the config toml file and will try to parse it in the structure.
+	   On successful parse, it will return the config structure. This is applicable for both
+	   AWS Provision and Deployment configuration both with Chef Managed and AWS Managed resources. */
+
+	fileUtils := &fileutils.FileSystemUtils{}
+	templateBytes, err := fileUtils.ReadFile(configFile)
+	if err != nil {
+		return nil, status.Wrap(err, status.FileAccessError, "error in reading config toml file")
+	}
+	config := HAAwsConfigToml{}
+	err = ptoml.Unmarshal(templateBytes, &config)
+	if err != nil {
+		return nil, status.Wrap(err, status.ConfigError, "error in unmarshalling config toml file")
+	}
+	return &config, nil
+}
+
+func ParseOnPremConfig(configFile string) (*HAOnPremConfigToml, error) {
+
+	/* This function will read the OnPrem config toml file and will try to parse it in the structure.
+	   On successful parse, it will return the config structure. This is applicable for Chef Managed,
+	   AWS Managed and Customer Managed resources*/
+
+	fileUtils := &fileutils.FileSystemUtils{}
+	templateBytes, err := fileUtils.ReadFile(configFile)
+	if err != nil {
+		return nil, status.Wrap(err, status.FileAccessError, "error in reading config toml file")
+	}
+	config := HAOnPremConfigToml{}
+	err = ptoml.Unmarshal(templateBytes, &config)
+	if err != nil {
+		return nil, status.Wrap(err, status.ConfigError, "error in unmarshalling config toml file")
+	}
+	return &config, nil
+}
+
+func ParseStandaloneConfig(configFile string) (*sc.AutomateConfig, error) {
+
+	/* This function will read the Standalone Automate config toml file and will try to parse it in the structure.
+	   On successful parse, it will return the config structure. */
+
+	config, err := sc.LoadUserOverrideConfigFile(configFile)
+	if err != nil {
+		return nil, err
+	}
+	return config, nil
+}
diff --git a/lib/config_parser/setupConfig.go b/lib/config_parser/setupConfig.go
new file mode 100644
index 000000000..167d14e0e
--- /dev/null
+++ b/lib/config_parser/setupConfig.go
@@ -0,0 +1,262 @@
+package config_parser
+
+type CertByIP struct {
+	IP         string `toml:"ip"`
+	PrivateKey string `toml:"private_key"`
+	PublicKey  string `toml:"public_key"`
+	NodesDn    string `toml:"nodes_dn,omitempty"`
+}
+
+type HAAwsConfigToml struct {
+	Architecture struct {
+		ConfigInitials struct {
+			SecretsKeyFile              string `toml:"secrets_key_file"`
+			SecretsStoreFile            string `toml:"secrets_store_file"`
+			Architecture                string `toml:"architecture"`
+			WorkspacePath               string `toml:"workspace_path"`
+			SSHUser                     string `toml:"ssh_user"`
+			SSHKeyFile                  string `toml:"ssh_key_file"`
+			SSHPort                     string `toml:"ssh_port"`
+			SudoPassword                string `toml:"sudo_password"`
+			LoggingMonitoringManagement string `toml:"logging_monitoring_management"`
+			NewElk                      string `toml:"new_elk"`
+			ExistingElkInstanceIP       string `toml:"existing_elk_instance_ip"`
+			ExistingElkPort             string `toml:"existing_elk_port"`
+			ExistingElkCert             string `toml:"existing_elk_cert"`
+			ExistingElkUsername         string `toml:"existing_elk_username"`
+			ExistingElkPassword         string `toml:"existing_elk_password"`
+			BackupMount                 string `toml:"backup_mount"`
+			BackupConfig                string `toml:"backup_config"`
+			S3BucketName                string `toml:"s3_bucketName"`
+			HabitatUIDGid               string `toml:"habitat_uid_gid"`
+		} `toml:"aws"`
+	} `toml:"architecture"`
+	Automate struct {
+		Config struct {
+			AdminPassword     string     `toml:"admin_password"`
+			Fqdn              string     `toml:"fqdn"`
+			AutomateSetupType string     `toml:"automate_setup_type"`
+			InstanceCount     string     `toml:"instance_count"`
+			TeamsPort         string     `toml:"teams_port"`
+			ConfigFile        string     `toml:"config_file"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			RootCA            string     `toml:"root_ca"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"automate"`
+	ChefServer struct {
+		Config struct {
+			InstanceCount     string     `toml:"instance_count"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"chef_server"`
+	Opensearch struct {
+		Config struct {
+			InstanceCount     string     `toml:"instance_count"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			RootCA            string     `toml:"root_ca"`
+			AdminCert         string     `toml:"admin_cert"`
+			AdminKey          string     `toml:"admin_key"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			AdminDn           string     `toml:"admin_dn"`
+			NodesDn           string     `toml:"nodes_dn"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"opensearch"`
+	Postgresql struct {
+		Config struct {
+			InstanceCount     string     `toml:"instance_count"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			RootCA            string     `toml:"root_ca"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"postgresql"`
+	Aws struct {
+		Config struct {
+			Profile                      string   `toml:"profile"`
+			Region                       string   `toml:"region"`
+			AwsVpcId                     string   `toml:"aws_vpc_id"`
+			AwsCidrBlockAddr             string   `toml:"aws_cidr_block_addr"`
+			PrivateCustomSubnets         []string `toml:"private_custom_subnets"`
+			PublicCustomSubnets          []string `toml:"public_custom_subnets"`
+			SSHKeyPairName               string   `toml:"ssh_key_pair_name"`
+			SetupManagedServices         bool     `toml:"setup_managed_services"`
+			OpensearchDomainName         string   `toml:"managed_opensearch_domain_name"`
+			OpensearchDomainUrl          string   `toml:"managed_opensearch_domain_url"`
+			OpensearchUsername           string   `toml:"managed_opensearch_username"`
+			OpensearchUserPassword       string   `toml:"managed_opensearch_user_password"`
+			OpensearchCertificate        string   `toml:"managed_opensearch_certificate"`
+			AwsOsSnapshotRoleArn         string   `toml:"aws_os_snapshot_role_arn"`
+			OsUserAccessKeyId            string   `toml:"os_snapshot_user_access_key_id"`
+			OsUserAccessKeySecret        string   `toml:"os_snapshot_user_access_key_secret"`
+			RDSInstanceUrl               string   `toml:"managed_rds_instance_url"`
+			RDSSuperUserName             string   `toml:"managed_rds_superuser_username"`
+			RDSSuperUserPassword         string   `toml:"managed_rds_superuser_password"`
+			RDSDBUserName                string   `toml:"managed_rds_dbuser_username"`
+			RDSDBUserPassword            string   `toml:"managed_rds_dbuser_password"`
+			RDSCertificate               string   `toml:"managed_rds_certificate"`
+			AmiFilterName                string   `toml:"ami_filter_name"`
+			AmiFilterVirtType            string   `toml:"ami_filter_virt_type"`
+			AmiFilterOwner               string   `toml:"ami_filter_owner"`
+			AmiID                        string   `toml:"ami_id"`
+			LBAccessLogs                 string   `toml:"lb_access_logs"`
+			DeleteOnTermination          bool     `toml:"delete_on_termination"`
+			AutomateServerInstanceType   string   `toml:"automate_server_instance_type"`
+			ChefServerInstanceType       string   `toml:"chef_server_instance_type"`
+			OpensearchServerInstanceType string   `toml:"opensearch_server_instance_type"`
+			PostgresqlServerInstanceType string   `toml:"postgresql_server_instance_type"`
+			AutomateLbCertificateArn     string   `toml:"automate_lb_certificate_arn"`
+			ChefServerLbCertificateArn   string   `toml:"chef_server_lb_certificate_arn"`
+			AutomateEbsVolumeIops        string   `toml:"automate_ebs_volume_iops"`
+			AutomateEbsVolumeSize        string   `toml:"automate_ebs_volume_size"`
+			AutomateEbsVolumeType        string   `toml:"automate_ebs_volume_type"`
+			ChefEbsVolumeIops            string   `toml:"chef_ebs_volume_iops"`
+			ChefEbsVolumeSize            string   `toml:"chef_ebs_volume_size"`
+			ChefEbsVolumeType            string   `toml:"chef_ebs_volume_type"`
+			OpensearchEbsVolumeIops      string   `toml:"opensearch_ebs_volume_iops"`
+			OpensearchEbsVolumeSize      string   `toml:"opensearch_ebs_volume_size"`
+			OpensearchEbsVolumeType      string   `toml:"opensearch_ebs_volume_type"`
+			PostgresqlEbsVolumeIops      string   `toml:"postgresql_ebs_volume_iops"`
+			PostgresqlEbsVolumeSize      string   `toml:"postgresql_ebs_volume_size"`
+			PostgresqlEbsVolumeType      string   `toml:"postgresql_ebs_volume_type"`
+			XContact                     string   `toml:"X-Contact"`
+			XDept                        string   `toml:"X-Dept"`
+			XProject                     string   `toml:"X-Project"`
+			XProduction                  string   `toml:"X-Production"`
+			XCustomer                    string   `toml:"X-Customer"`
+			AwsAutomateRoute53Prefix     string   `toml:"aws_automate_route53_prefix"`
+			AwsChefServerRoute53Prefix   string   `toml:"aws_chef_server_route53_prefix"`
+			AwsRoute53HostedZone         string   `toml:"aws_route53_hosted_zone"`
+			PostrgesqlDbIdentifier       string   `toml:"postgresql_db_identifier"`
+			ElasticsearchDomainName      string   `toml:"elasticsearch_domain_name"`
+			RDSInstanceType              string   `toml:"rds_postgresql_instance_type"`
+			RDSRestoreIdentifier         string   `toml:"rds_postgresql_restore_identifier"`
+			DatadogAPIKey                string   `toml:"datadog_api_key"`
+			UseExistingManagedInfra      bool     `toml:"use_existing_managed_infra"`
+		} `toml:"config"`
+	} `toml:"aws"`
+}
+
+type HAOnPremConfigToml struct {
+	Architecture struct {
+		ConfigInitials struct {
+			SecretsKeyFile              string `toml:"secrets_key_file,omitempty"`
+			SecretsStoreFile            string `toml:"secrets_store_file,omitempty"`
+			Architecture                string `toml:"architecture,omitempty"`
+			WorkspacePath               string `toml:"workspace_path,omitempty"`
+			SSHUser                     string `toml:"ssh_user,omitempty"`
+			SSHKeyFile                  string `toml:"ssh_key_file,omitempty"`
+			SSHPort                     string `toml:"ssh_port,omitempty"`
+			SudoPassword                string `toml:"sudo_password,omitempty"`
+			LoggingMonitoringManagement string `toml:"logging_monitoring_management,omitempty"`
+			NewElk                      string `toml:"new_elk,omitempty"`
+			ExistingElkInstanceIP       string `toml:"existing_elk_instance_ip,omitempty"`
+			ExistingElkPort             string `toml:"existing_elk_port,omitempty"`
+			ExistingElkCert             string `toml:"existing_elk_cert,omitempty"`
+			ExistingElkUsername         string `toml:"existing_elk_username,omitempty"`
+			ExistingElkPassword         string `toml:"existing_elk_password,omitempty"`
+			BackupMount                 string `toml:"backup_mount,omitempty"`
+			HabitatUIDGid               string `toml:"habitat_uid_gid,omitempty"`
+			BackupConfig                string `toml:"backup_config,omitempty"`
+		} `toml:"existing_infra"`
+	} `toml:"architecture"`
+	Automate struct {
+		Config struct {
+			AdminPassword     string     `toml:"admin_password"`
+			Fqdn              string     `toml:"fqdn"`
+			InstanceCount     string     `toml:"instance_count"`
+			TeamsPort         string     `toml:"teams_port,omitempty"`
+			ConfigFile        string     `toml:"config_file"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			RootCA            string     `toml:"root_ca"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"automate"`
+	ChefServer struct {
+		Config struct {
+			InstanceCount     string     `toml:"instance_count"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"chef_server"`
+	Opensearch struct {
+		Config struct {
+			InstanceCount     string     `toml:"instance_count"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			RootCA            string     `toml:"root_ca"`
+			AdminCert         string     `toml:"admin_cert"`
+			AdminKey          string     `toml:"admin_key"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			AdminDn           string     `toml:"admin_dn"`
+			NodesDn           string     `toml:"nodes_dn"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"opensearch"`
+	Postgresql struct {
+		Config struct {
+			InstanceCount     string     `toml:"instance_count"`
+			EnableCustomCerts bool       `toml:"enable_custom_certs"`
+			RootCA            string     `toml:"root_ca"`
+			PrivateKey        string     `toml:"private_key"`
+			PublicKey         string     `toml:"public_key"`
+			CertsByIP         []CertByIP `toml:"certs_by_ip"`
+		} `toml:"config"`
+	} `toml:"postgresql"`
+	ExistingInfra struct {
+		Config struct {
+			AutomatePrivateIps   []string `toml:"automate_private_ips"`
+			ChefServerPrivateIps []string `toml:"chef_server_private_ips"`
+			OpensearchPrivateIps []string `toml:"opensearch_private_ips"`
+			PostgresqlPrivateIps []string `toml:"postgresql_private_ips"`
+		} `toml:"config"`
+	} `toml:"existing_infra"`
+	ExternalDB struct {
+		Database struct {
+			Type       string `toml:"type"`
+			PostgreSQL struct {
+				PostgreSQLInstanceURL       string `toml:"instance_url"`
+				PostgreSQLSuperUserName     string `toml:"superuser_username"`
+				PostgreSQLSuperUserPassword string `toml:"superuser_password"`
+				PostgreSQLDBUserName        string `toml:"dbuser_username"`
+				PostgreSQLDBUserPassword    string `toml:"dbuser_password"`
+				PostgreSQLCertificate       string `toml:"postgresql_certificate"`
+				PostgreSQLRootCert          string `toml:"postgresql_root_cert"`
+			} `toml:"postgre_sql"`
+			Opensearch struct {
+				OpensearchInstanceURL       string `toml:"opensearch_domain_url"`
+				OpensearchSuperUserName     string `toml:"opensearch_username"`
+				OpensearchSuperUserPassword string `toml:"opensearch_user_password"`
+				OpensearchCertificate       string `toml:"opensearch_certificate"`
+				OpensearchRootCert          string `toml:"opensearch_root_cert"`
+				OpensearchDomainName        string `toml:"opensearch_domain_name"`
+				AWS                         struct {
+					AwsOsSnapshotRoleArn  string `toml:"aws_os_snapshot_role_arn"`
+					OsUserAccessKeyId     string `toml:"os_snapshot_user_access_key_id"`
+					OsUserAccessKeySecret string `toml:"os_snapshot_user_access_key_secret"`
+				} `toml:"aws"`
+			} `toml:"open_search"`
+		} `toml:"database"`
+	} `toml:"external"`
+	ObjectStorage struct {
+		Config struct {
+			BucketName string `toml:"bucket_name"`
+			AccessKey  string `toml:"access_key"`
+			SecretKey  string `toml:"secret_key"`
+			Endpoint   string `toml:"endpoint"`
+			Region     string `toml:"region"`
+		} `toml:"config"`
+	} `toml:"object_storage"`
+}
diff --git a/lib/reporting/helperForVerification.go b/lib/reporting/helperForVerification.go
new file mode 100644
index 000000000..cef0e25c1
--- /dev/null
+++ b/lib/reporting/helperForVerification.go
@@ -0,0 +1,312 @@
+package reporting
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/jedib0t/go-pretty/v5/table"
+)
+
+type VerificationReport struct {
+	TableKey     string
+	Report       Info
+	TotalReports int
+}
+
+type Info struct {
+	Hostip        string         `json:"hostip,omitempty"`
+	Parameter     string         `json:"parameter,omitempty"`
+	Status        string         `json:"status,omitempty"`
+	StatusMessage *StatusMessage `json:"statusMessage,omitempty"`
+	SummaryInfo   *SummaryInfo   `json:"summaryInfo,omitempty"`
+}
+
+type StatusMessage struct {
+	MainMessage string   `json:"mainMessage,omitempty"`
+	SubMessage  []string `json:"subMessage,omitempty"`
+}
+
+type SummaryInfo struct {
+	SuccessfulCount int      `json:"successfulCount,omitempty"`
+	FailedCount     int      `json:"failedCount,omitempty"`
+	ToResolve       []string `json:"toResolve,omitempty"`
+}
+
+type progress struct {
+	currentCount int
+	totalCount   int
+}
+
+func VerificationReports(reportChan chan VerificationReport, reporting Reporting, nodeInfoMap map[string][]Info, done chan bool) {
+	tableProgress := make(map[string]progress)
+	keyMap := make(map[string][]string)
+	tableKeys := reporting.GetAllTableKeys()
+	oldProgresslen := 0
+	for n := range reportChan {
+		time.Sleep(time.Second * 1)
+		report := n
+		info := report.Report
+		total := report.TotalReports
+		node := report.TableKey
+		var nodeinfo []Info
+		if _, ok := nodeInfoMap[node]; ok {
+			nodeinfo := nodeInfoMap[node]
+			nodeinfo = append(nodeinfo, info)
+			nodeInfoMap[node] = nodeinfo
+			oldCount := tableProgress[node].currentCount
+			tableProgress[node] = progress{
+				currentCount: oldCount + 1,
+				totalCount:   total,
+			}
+		} else {
+			nodeinfo = append(nodeinfo, info)
+			nodeInfoMap[node] = nodeinfo
+			keyMap[node] = matchNodeNameWithRespectiveTablesName(node, tableKeys)
+			tableProgress[node] = progress{
+				currentCount: 1,
+				totalCount:   total,
+			}
+		}
+		oldProgresslen = showProgress(tableProgress, oldProgresslen)
+	}
+	clearPrintedProgressMsg(len(tableProgress))
+	updateTablesWithData(reporting, nodeInfoMap, keyMap)
+	printTables(reporting, nodeInfoMap, keyMap)
+	done <- true
+}
+
+func printTables(reporting Reporting, nodeInfo map[string][]Info, keyMap map[string][]string) {
+	var summaryTables []*Table
+	keys := make([]string, len(nodeInfo))
+	i := 0
+	for key := range nodeInfo {
+		keys[i] = key
+		i++
+	}
+	sort.Strings(keys)
+	updateTableTitle(reporting, nodeInfo, keyMap)
+	for _, key := range keys {
+		statusTb := reporting.GetTable(keyMap[key][0])
+		summaryTb := reporting.GetTable(keyMap[key][1])
+		summaryTables = append(summaryTables, summaryTb)
+		printSingleTable(reporting, statusTb)
+	}
+	for _, summaryTable := range summaryTables {
+		printSingleTable(reporting, summaryTable)
+	}
+}
+
+func printSingleTable(reporting Reporting, table *Table) {
+	reporting.GenerateTableOutputAndPrint(table)
+	fmt.Print("\n")
+}
+
+func updateTablesWithData(reporting Reporting, nodeInfo map[string][]Info, keyMap map[string][]string) {
+	for key, value := range nodeInfo {
+		summaryMap := make(map[string]SummaryInfo)
+		statusTableRows, summaryTableRows := createSingleNodeRowsForBothTables(key, value, reporting, summaryMap)
+		reporting.GetTable(keyMap[key][0]).Rows = statusTableRows
+		reporting.GetTable(keyMap[key][1]).Rows = summaryTableRows
+	}
+}
+
+func createSingleNodeRowsForBothTables(title string, nodeInfo []Info, reporting Reporting, summaryMap map[string]SummaryInfo) ([]table.Row, []table.Row) {
+	var statusTableRows []table.Row
+	var summaryTableRows []table.Row
+	for idx, value := range nodeInfo {
+		singleRowEntry := createStatusTableRows(idx+1, &value, reporting)
+		statusTableRows = append(statusTableRows, singleRowEntry...)
+		summaryMap = createSummaryTableRowData(summaryMap, &value)
+	}
+	summaryTableRows = createSummaryTableRows(summaryMap, reporting)
+	return statusTableRows, summaryTableRows
+}
+
+func createStatusTableRows(index int, rowData *Info, reporting Reporting) []table.Row {
+	var row table.Row
+	var rows []table.Row
+
+	if rowData.Status == "Success" {
+		row = successRow(index, rowData, reporting)
+		rows = append(rows, row)
+	} else if rowData.Status == "Failed" {
+
+		row = failedRow(index, rowData, reporting)
+		rows = append(rows, row)
+	}
+	if len(rowData.StatusMessage.SubMessage) > 0 {
+		for _, value := range rowData.StatusMessage.SubMessage {
+			row = subMessageRow(value, rowData, reporting)
+			rows = append(rows, row)
+		}
+	}
+	return rows
+}
+
+func successRow(index int, rowData *Info, reporting Reporting) table.Row {
+	hostip := rowData.Hostip
+	parameter := rowData.Parameter
+	status := reporting.ChangeColour(Green, reporting.AppendSpecialCharater(Success, rowData.Status))
+	message := rowData.StatusMessage.MainMessage
+	row := table.Row{index, hostip, parameter, status, message}
+	return row
+}
+
+func failedRow(index int, rowData *Info, reporting Reporting) table.Row {
+	hostip := reporting.ChangeColour(Red, rowData.Hostip)
+	parameter := reporting.ChangeColour(Red, rowData.Parameter)
+	status := reporting.ChangeColour(Red, reporting.AppendSpecialCharater(Failed, rowData.Status))
+	message := reporting.ChangeColour(Red, rowData.StatusMessage.MainMessage)
+
+	row := table.Row{index, hostip, parameter, status, message}
+	return row
+}
+
+func subMessageRow(subMsg string, rowData *Info, reporting Reporting) table.Row {
+	var newSubMessage string
+	if rowData.Status == "Failed" {
+		key := reporting.AppendSpecialCharater(Failed, reporting.AppendSpecialCharater(SquareBracket, rowData.Status))
+		newSubMessage = reporting.ChangeColour(Red, key+subMsg)
+	} else {
+		key := reporting.ChangeColour(Yellow, reporting.AppendSpecialCharater(Warning, reporting.AppendSpecialCharater(SquareBracket, "Warning")))
+		newSubMessage = key + subMsg
+	}
+	row := table.Row{"", "", "", "", newSubMessage}
+	return row
+}
+
+func createSummaryTableRowData(summary map[string]SummaryInfo, rowData *Info) map[string]SummaryInfo {
+	var toResolve []string
+	if rowData.SummaryInfo.ToResolve != nil && len(rowData.SummaryInfo.ToResolve) > 0 {
+		toResolve = []string{fmt.Sprintf("%s:", rowData.Hostip)}
+		rowData.SummaryInfo.ToResolve = createIndexForToResolve(rowData.SummaryInfo.ToResolve)
+		toResolve = append(toResolve, rowData.SummaryInfo.ToResolve...)
+	}
+	if _, ok := summary[rowData.Parameter]; ok {
+		singleParameterSummary := summary[rowData.Parameter]
+		singleParameterSummary.SuccessfulCount = singleParameterSummary.SuccessfulCount + rowData.SummaryInfo.SuccessfulCount
+		singleParameterSummary.FailedCount = singleParameterSummary.FailedCount + rowData.SummaryInfo.FailedCount
+		singleParameterSummary.ToResolve = append(singleParameterSummary.ToResolve, toResolve...)
+
+		summary[rowData.Parameter] = singleParameterSummary
+		return summary
+	}
+
+	summary[rowData.Parameter] = SummaryInfo{
+		SuccessfulCount: rowData.SummaryInfo.SuccessfulCount,
+		FailedCount:     rowData.SummaryInfo.FailedCount,
+		ToResolve:       toResolve,
+	}
+
+	return summary
+}
+
+func createSummaryTableRows(rowData map[string]SummaryInfo, reporting Reporting) []table.Row {
+	var rows []table.Row
+	for key, value := range rowData {
+		if key != "" {
+			singleParameterRow := createSingleParameterRows(key, value, reporting)
+			rows = append(rows, singleParameterRow...)
+		}
+	}
+	return rows
+}
+
+func createSingleParameterRows(parameter string, parameterSummary SummaryInfo, reporting Reporting) []table.Row {
+	var row table.Row
+	var rows []table.Row
+	successCount := reporting.ChangeColour(Green, fmt.Sprintf("%d", parameterSummary.SuccessfulCount))
+	failedCount := reporting.ChangeColour(Red, fmt.Sprintf("%d", parameterSummary.FailedCount))
+	toResolve := parameterSummary.ToResolve
+	if len(toResolve) > 1 {
+		row = table.Row{parameter, successCount, failedCount, toResolve[0]}
+		rows = append(rows, row)
+		for _, value := range toResolve[1:] {
+			row = table.Row{"", "", "", value}
+			rows = append(rows, row)
+		}
+	} else {
+		row = table.Row{parameter, successCount, failedCount, ""}
+		rows = append(rows, row)
+	}
+	return rows
+}
+
+func showProgress(tableProgess map[string]progress, lines int) int {
+	clearPrintedProgressMsg(lines)
+	keys := make([]string, len(tableProgess))
+	i := 0
+	for key := range tableProgess {
+		keys[i] = key
+		i++
+	}
+	sort.Strings(keys)
+	for _, key := range keys {
+		percent := float64(tableProgess[key].currentCount) / float64(tableProgess[key].totalCount) * 100
+		progressMsg := fmt.Sprintf("%-12s %.2f%%\n", key, percent)
+		fmt.Print(progressMsg)
+	}
+	return len(keys)
+}
+
+func clearPrintedProgressMsg(lines int) {
+	if lines > 0 {
+		fmt.Printf("\033[%dA\033[J", lines)
+	}
+}
+
+func updateTableTitle(reporting Reporting, nodeInfo map[string][]Info, keyMap map[string][]string) {
+	var failed bool
+	for key, value := range nodeInfo {
+		title := reporting.GetTable(keyMap[key][0]).Title
+		if checkStatusNotEmpty(value) {
+			return
+		}
+		failed = checkFailedStatus(value)
+		if failed {
+			updatedTile := reporting.ChangeColour(Red, reporting.AppendSpecialCharater(Failed, title))
+			reporting.GetTable(keyMap[key][0]).Title = updatedTile
+		} else {
+			updatedTile := reporting.ChangeColour(Green, reporting.AppendSpecialCharater(Success, title))
+			reporting.GetTable(keyMap[key][0]).Title = updatedTile
+		}
+	}
+}
+
+func checkStatusNotEmpty(info []Info) bool {
+	for _, v := range info {
+		if v.Status == "" {
+			return true
+		}
+	}
+	return false
+}
+
+func checkFailedStatus(info []Info) bool {
+	for _, v := range info {
+		if v.Status == "Failed" {
+			return true
+		}
+	}
+	return false
+}
+
+func matchNodeNameWithRespectiveTablesName(substring string, keys []string) []string {
+	var matchingKeys []string
+	for _, key := range keys {
+		if strings.Contains(key, substring) {
+			matchingKeys = append(matchingKeys, key)
+		}
+	}
+	sort.Strings(matchingKeys)
+	return matchingKeys
+}
+
+func createIndexForToResolve(data []string) []string {
+	for key, value := range data {
+		data[key] = fmt.Sprintf("%d. %s", key+1, value)
+	}
+	return data
+}
diff --git a/lib/reporting/reporting.go b/lib/reporting/reporting.go
new file mode 100644
index 000000000..b054252a6
--- /dev/null
+++ b/lib/reporting/reporting.go
@@ -0,0 +1,130 @@
+package reporting
+
+import (
+	"sort"
+
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/fatih/color"
+	"github.com/jedib0t/go-pretty/v5/table"
+)
+
+const (
+	Red = iota
+	Green
+	Yellow
+)
+
+const (
+	Failed = iota
+	Success
+	Warning
+	SquareBracket
+)
+
+type Reporting interface {
+	GetTable(key string) *Table
+	SetTable(key string, table *Table)
+	GetAllTableKeys() []string
+	GetAllTables() map[string]*Table
+	AppendSpecialCharater(symbol int, str string) string
+	ChangeColour(fgColor int, msg string) string
+	GenerateTableOutputAndPrint(table *Table)
+}
+
+type Table struct {
+	Title     string
+	Rows      []table.Row
+	Header    table.Row
+	Footer    table.Row
+	ColConfig []table.ColumnConfig
+}
+
+type ReportingModule struct {
+	writer *cli.Writer
+	tables map[string]*Table
+}
+
+func NewReportingModule(wr *cli.Writer, tables map[string]*Table) Reporting {
+	newReportingModule := &ReportingModule{
+		writer: wr,
+		tables: tables,
+	}
+	return newReportingModule
+}
+
+// GetTable returns table present in tables map for given key.
+func (r *ReportingModule) GetTable(key string) *Table {
+	return r.tables[key]
+}
+
+// GetAllTableKeys returns all keys present in tables map
+func (r *ReportingModule) GetAllTableKeys() []string {
+	keys := make([]string, len(r.tables))
+	i := 0
+	for key := range r.tables {
+		keys[i] = key
+		i++
+	}
+	sort.Strings(keys)
+	return keys
+}
+
+// GetAllTables returns tables map
+func (r *ReportingModule) GetAllTables() map[string]*Table {
+	return r.tables
+}
+
+// SetTable sets the value of tables[key] in tables map to table provided in argument
+func (r *ReportingModule) SetTable(key string, table *Table) {
+	r.tables[key] = table
+}
+
+// AppendSpecialCharater returns a string.
+// Function appends a special char at the start of the str provided in arguments and returns that string
+func (r *ReportingModule) AppendSpecialCharater(symbol int, str string) string {
+	failedSymbol := "✖ "
+	successSymbol := "✔ "
+	warningSymbol := "! "
+
+	switch symbol {
+	case Failed:
+		msg := failedSymbol + str
+		return msg
+	case Success:
+		msg := successSymbol + str
+		return msg
+	case Warning:
+		msg := warningSymbol + str
+		return msg
+	case SquareBracket:
+		msg := "[ " + str + " ] "
+		return msg
+	}
+
+	return str
+}
+
+// ChangeColour returns a string.
+// Function changes the color of the msg provided in arguments to the color given
+func (r *ReportingModule) ChangeColour(fgColor int, msg string) string {
+	switch fgColor {
+	case Red:
+		return color.New(color.FgRed).Sprint(msg)
+	case Green:
+		return color.New(color.FgGreen).Sprint(msg)
+	case Yellow:
+		return color.New(color.FgYellow).Sprint(msg)
+	default:
+		return msg
+	}
+}
+
+// GenerateTableOutputAndPrint prints table provided as argument
+func (r *ReportingModule) GenerateTableOutputAndPrint(tb *Table) {
+	tbWriter := table.NewWriter()
+	tbWriter.SetColumnConfigs(tb.ColConfig)
+	tbWriter.AppendHeader(tb.Header)
+	tbWriter.AppendRows(tb.Rows)
+	r.writer.Println(tb.Title)
+	r.writer.Println(tbWriter.Render())
+}
diff --git a/lib/verification/checks.go b/lib/verification/checks.go
new file mode 100644
index 000000000..849059ab9
--- /dev/null
+++ b/lib/verification/checks.go
@@ -0,0 +1,271 @@
+package verification
+
+import (
+	sc "github.com/chef/automate/api/config/deployment"
+	"github.com/chef/automate/lib/config_parser"
+	"github.com/chef/automate/lib/reporting"
+)
+
+const (
+	systemResources  = "System Resources"
+	softwareVersions = "Software Versions"
+)
+
+type Result struct {
+	Valid  bool
+	Report reporting.Info
+}
+
+func validateAWSProvisionConfig(config *config_parser.HAAwsConfigToml) {
+
+	/* Write the checks to validate if all the required fields for AWS Infra Provision without Managed Services
+	are filled in the parsed config */
+
+	if config.Aws.Config.SetupManagedServices {
+		/* Write the checks to validate if all the required fields for AWS Infra Provision with Managed Services
+		are filled in the parsed config */
+	}
+
+}
+
+func validateAWSManagedProvisionConfig(config *config_parser.HAAwsConfigToml) {
+
+	/* Write the checks to validate if all the required fields for AWS Infra Provision with Managed Services
+	are filled in the parsed config */
+
+}
+
+func validateAWSDeploymentConfig(config *config_parser.HAAwsConfigToml) {
+
+	/* Write the checks to validate if all the required fields for AWS Deployment without Managed Services
+	are filled in the parsed config */
+
+}
+
+func validateAWSManagedDeploymentConfig(config *config_parser.HAAwsConfigToml) {
+
+	/* Write the checks to validate if all the required fields for AWS Deployment with Managed Services
+	are filled in the parsed config */
+
+}
+
+func validateOnPremConfig(config *config_parser.HAOnPremConfigToml) {
+
+	/* Write the checks to validate if all the required fields for On-Prem Deployment with Chef Managed Services
+	are filled in the parsed config */
+
+}
+
+func validateOnPremAWSConfig(config *config_parser.HAOnPremConfigToml) {
+
+	/* Write the checks to validate if all the required fields for On-Prem Deployment with AWS Managed Services
+	are filled in the parsed config */
+
+}
+
+func validateOnPremCustomerConfig(config *config_parser.HAOnPremConfigToml) {
+
+	/* Write the checks to validate if all the required fields for On-Prem Deployment with Customer Managed Services
+	are filled in the parsed config */
+
+}
+
+func validateStandaloneDeploymentConfig(config *sc.AutomateConfig) {
+
+	/* Write the checks to validate if all the required fields for On-Prem Deployment with Customer Managed Services
+	are filled in the parsed config */
+
+}
+
+func validateNodeReachability(ipaddress string, nodeType string, channel chan reporting.VerificationReport, numberOfNodes *numberOfNodes) {
+
+	/* Write the checks to validate if the nodes are reachable */
+
+	result := reporting.Info{
+		Hostip:    ipaddress,
+		Parameter: "Node Reachabilility",
+		Status:    "Success",
+		StatusMessage: &reporting.StatusMessage{
+			MainMessage: "Node " + ipaddress + " is reachable",
+		},
+		SummaryInfo: &reporting.SummaryInfo{
+			SuccessfulCount: 1,
+		},
+	}
+
+	chanWriter(channel, nodeType, result, numberOfNodes)
+
+}
+
+func validateCertificateExpiry(ipaddress string, certContents string) Result {
+
+	return Result{
+		Valid: true,
+		Report: reporting.Info{
+			Hostip:    ipaddress,
+			Parameter: "Certificates",
+			Status:    "Success",
+			StatusMessage: &reporting.StatusMessage{
+				MainMessage: "Certificate A validated successfully",
+			},
+		},
+	}
+
+	/* Write the checks to validate the expiry date of the certificate */
+
+}
+
+func validateCertificateFormat(ipaddress string, certContents string) Result {
+
+	return Result{
+		Valid: true,
+		Report: reporting.Info{
+			Hostip:    ipaddress,
+			Parameter: "Certificates",
+			Status:    "Failed",
+			StatusMessage: &reporting.StatusMessage{
+				MainMessage: "Certificate B validation failed",
+				SubMessage:  []string{"Certificate is not formatted properly"},
+			},
+			SummaryInfo: &reporting.SummaryInfo{
+				ToResolve: []string{"Add the start and end certificate markers"},
+			},
+		},
+	}
+
+	/* Write the checks to validate the expiry date of the certificate */
+
+}
+
+func validateFreeDisk(ipaddress string, nodeType string) Result {
+
+	if nodeType == "OpenSearch" {
+		return Result{
+			Valid: false,
+			Report: reporting.Info{
+				Hostip:    ipaddress,
+				Parameter: systemResources,
+				Status:    "Failed",
+				StatusMessage: &reporting.StatusMessage{
+					MainMessage: "System Resources check failed",
+					SubMessage:  []string{"/hab volume is not of 100 GB"},
+				},
+				SummaryInfo: &reporting.SummaryInfo{
+					ToResolve: []string{"Re-mount the hab volume with 100 GB"},
+				},
+			},
+		}
+	}
+	return Result{
+		Valid: true,
+		Report: reporting.Info{
+			Hostip:    ipaddress,
+			Parameter: systemResources,
+			Status:    "Success",
+			StatusMessage: &reporting.StatusMessage{
+				MainMessage: "System Resources as per the requirement",
+			},
+		},
+	}
+
+}
+
+func validateCPU(ipaddress string, nodeType string) Result {
+
+	if nodeType == "ChefServer" {
+		return Result{
+			Valid: true,
+			Report: reporting.Info{
+				Hostip:    ipaddress,
+				Parameter: systemResources,
+				Status:    "Success",
+				StatusMessage: &reporting.StatusMessage{
+					MainMessage: "System Resources as per the requirement",
+				},
+			},
+		}
+	}
+	return Result{
+		Valid: false,
+		Report: reporting.Info{
+			Hostip:    ipaddress,
+			Parameter: systemResources,
+			Status:    "Failed",
+			StatusMessage: &reporting.StatusMessage{
+				MainMessage: "System Resources check failed",
+				SubMessage:  []string{"CPU count is not as per the requirement"},
+			},
+			SummaryInfo: &reporting.SummaryInfo{
+				ToResolve: []string{"Increase the CPU of the machine. Machine should have 4 CPUs"},
+			},
+		},
+	}
+
+}
+
+func validateOSVersion(ipaddress string, nodeType string) Result {
+
+	if nodeType == "Automate" {
+		return Result{
+			Valid: false,
+			Report: reporting.Info{
+				Hostip:    ipaddress,
+				Parameter: softwareVersions,
+				Status:    "Failed",
+				StatusMessage: &reporting.StatusMessage{
+					MainMessage: "Software Versions check Failed",
+					SubMessage:  []string{"OS version is not Ubuntu 20.04"},
+				},
+				SummaryInfo: &reporting.SummaryInfo{
+					ToResolve: []string{"Setup the machine with Ubuntu 20.04"},
+				},
+			},
+		}
+	}
+	return Result{
+		Valid: true,
+		Report: reporting.Info{
+			Hostip:    ipaddress,
+			Parameter: softwareVersions,
+			Status:    "Success",
+			StatusMessage: &reporting.StatusMessage{
+				MainMessage: "Software Versions check Passed",
+			},
+		},
+	}
+
+}
+
+func validateLinuxCommands(ipaddress string, nodeType string) Result {
+
+	if nodeType == "OpenSearch" {
+		return Result{
+			Valid: true,
+			Report: reporting.Info{
+				Hostip:    ipaddress,
+				Parameter: softwareVersions,
+				Status:    "Success",
+				StatusMessage: &reporting.StatusMessage{
+					MainMessage: "Software Versions check Failed",
+					SubMessage:  []string{"make command failed"},
+				},
+				SummaryInfo: &reporting.SummaryInfo{
+					ToResolve: []string{"Add the required paths in the PATH folder"},
+				},
+			},
+		}
+	}
+
+	return Result{
+		Valid: true,
+		Report: reporting.Info{
+			Hostip:    ipaddress,
+			Parameter: softwareVersions,
+			Status:    "Success",
+			StatusMessage: &reporting.StatusMessage{
+				MainMessage: "Software Versions check Passed",
+			},
+		},
+	}
+
+}
diff --git a/lib/verification/mockVerify.go b/lib/verification/mockVerify.go
new file mode 100644
index 000000000..b8318648b
--- /dev/null
+++ b/lib/verification/mockVerify.go
@@ -0,0 +1,49 @@
+package verification
+
+type VerificationMock struct {
+	VerifyHAAWSProvisionFunc              func(configFile string) error
+	VerifyHAAWSManagedProvisionFunc       func(configFile string) error
+	VerifyHAAWSDeploymentFunc             func(configFile string) error
+	VerifyHAAWSManagedDeploymentFunc      func(configFile string) error
+	VerifyOnPremDeploymentFunc            func(configFile string) error
+	VerifyOnPremAWSManagedDeploymentFunc  func(configFile string) error
+	VerifyOnPremCustManagedDeploymentFunc func(configFile string) error
+	VerifyStandaloneDeploymentFunc        func(configFile string) error
+	VerifyCertificatesFunc                func(configFile string) error
+}
+
+func (v *VerificationMock) VerifyHAAWSProvision(configFile string) error {
+	return v.VerifyHAAWSProvisionFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyHAAWSManagedProvision(configFile string) error {
+	return v.VerifyHAAWSManagedProvisionFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyHAAWSDeployment(configFile string) error {
+	return v.VerifyHAAWSDeploymentFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyHAAWSManagedDeployment(configFile string) error {
+	return v.VerifyHAAWSManagedDeploymentFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyOnPremDeployment(configFile string) error {
+	return v.VerifyOnPremDeploymentFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyOnPremAWSManagedDeployment(configFile string) error {
+	return v.VerifyOnPremAWSManagedDeploymentFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyOnPremCustManagedDeployment(configFile string) error {
+	return v.VerifyOnPremCustManagedDeploymentFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyStandaloneDeployment(configFile string) error {
+	return v.VerifyStandaloneDeploymentFunc(configFile)
+}
+
+func (v *VerificationMock) VerifyCertificates(configFile string) error {
+	return v.VerifyCertificatesFunc(configFile)
+}
diff --git a/lib/verification/verify.go b/lib/verification/verify.go
new file mode 100644
index 000000000..2684228d0
--- /dev/null
+++ b/lib/verification/verify.go
@@ -0,0 +1,425 @@
+package verification
+
+import (
+	"fmt"
+	"os"
+	"sync"
+
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/chef/automate/lib/config_parser"
+	"github.com/chef/automate/lib/reporting"
+	"github.com/jedib0t/go-pretty/v5/table"
+)
+
+type Verification interface {
+	VerifyHAAWSProvision(configFile string) error
+	VerifyHAAWSManagedProvision(configFile string) error
+	VerifyHAAWSDeployment(configFile string) error
+	VerifyHAAWSManagedDeployment(configFile string) error
+	VerifyOnPremDeployment(configFile string) error
+	VerifyOnPremAWSManagedDeployment(configFile string) error
+	VerifyOnPremCustManagedDeployment(configFile string) error
+	VerifyStandaloneDeployment(configFile string) error
+	VerifyCertificates(configFile string) error
+}
+
+type VerificationModule struct{}
+
+type report struct {
+	ipaddress             string
+	parameter             string
+	message               string
+	errors                []string
+	resolutions           []string
+	successfulChecksCount int
+	failedChecksCount     int
+}
+
+type numberOfNodes struct {
+	numberOfAutomateNodes    int
+	numberOfChefServerNodes  int
+	numberOfPostgresSQLNodes int
+	numberOfOpenSearchNodes  int
+}
+
+var reportChan = make(chan reporting.VerificationReport)
+var nodeInfoMap = make(map[string][]reporting.Info)
+var doneChan = make(chan bool, 1)
+
+func (v *VerificationModule) VerifyHAAWSProvision(configFile string) error {
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseAWSAutomateConfig(configFile)
+	if err != nil {
+		return err
+	}
+
+	validateAWSProvisionConfig(config)
+	return nil
+}
+
+func (v *VerificationModule) VerifyHAAWSManagedProvision(configFile string) error {
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseAWSAutomateConfig(configFile)
+	if err != nil {
+		return err
+	}
+	validateAWSManagedProvisionConfig(config)
+	return nil
+}
+
+func (v *VerificationModule) VerifyHAAWSDeployment(configFile string) error {
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseAWSAutomateConfig(configFile)
+	if err != nil {
+		return err
+	}
+	validateAWSDeploymentConfig(config)
+	return nil
+}
+
+func (v *VerificationModule) VerifyHAAWSManagedDeployment(configFile string) error {
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseAWSAutomateConfig(configFile)
+	if err != nil {
+		return err
+	}
+	validateAWSManagedDeploymentConfig(config)
+	return nil
+}
+
+func (v *VerificationModule) VerifyOnPremAWSManagedDeployment(configFile string) error {
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseOnPremConfig(configFile)
+	if err != nil {
+		return err
+	}
+	validateOnPremAWSConfig(config)
+	return nil
+}
+
+func (v *VerificationModule) VerifyOnPremCustManagedDeployment(configFile string) error {
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseOnPremConfig(configFile)
+	if err != nil {
+		return err
+	}
+	validateOnPremCustomerConfig(config)
+	return nil
+}
+
+func (v *VerificationModule) VerifyStandaloneDeployment(configFile string) error {
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseStandaloneConfig(configFile)
+	if err != nil {
+		return err
+	}
+	validateStandaloneDeploymentConfig(config)
+	return nil
+}
+
+func (v *VerificationModule) VerifyCertificates(certContents string) error {
+
+	return nil
+}
+
+func (v *VerificationModule) VerifyOnPremDeployment(configFile string) error {
+
+	configParser := &config_parser.ConfigParserImpl{}
+	config, err := configParser.ParseOnPremConfig(configFile)
+	if err != nil {
+		return err
+	}
+	var ipsMap = make(map[string]string)
+	automateIps := config.ExistingInfra.Config.AutomatePrivateIps
+	chefServerIps := config.ExistingInfra.Config.ChefServerPrivateIps
+	opensearchIps := config.ExistingInfra.Config.OpensearchPrivateIps
+	postgresIps := config.ExistingInfra.Config.PostgresqlPrivateIps
+
+	dataMap := map[string][]string{
+		"Automate":    automateIps,
+		"ChefServer":  chefServerIps,
+		"OpenSearch":  opensearchIps,
+		"PostgresSQL": postgresIps,
+	}
+
+	for nodeType, nodeIps := range dataMap {
+		constructMap(nodeIps, nodeType, &ipsMap)
+	}
+
+	numberOfNodes := &numberOfNodes{
+		numberOfAutomateNodes:    len(automateIps),
+		numberOfChefServerNodes:  len(chefServerIps),
+		numberOfPostgresSQLNodes: len(postgresIps),
+		numberOfOpenSearchNodes:  len(opensearchIps),
+	}
+
+	startReportModule(numberOfNodes)
+
+	//validateOnPremConfig(config)
+
+	runTests(ipsMap, numberOfNodes)
+
+	return nil
+}
+
+func constructMap(nodeIps []string, nodeType string, m *map[string]string) {
+	for _, val := range nodeIps {
+		(*m)[val] = nodeType
+	}
+}
+
+func runTests(ipsMap map[string]string, numberOfNodes *numberOfNodes) {
+	var wg sync.WaitGroup
+
+	for k, v := range ipsMap {
+		wg.Add(3)
+		ip := fmt.Sprintf("%v", k)
+		nodeType := fmt.Sprintf("%v", v)
+		if nodeType == "OpenSearch" || nodeType == "PostgresSQL" {
+			wg.Add(1)
+			go func() {
+				defer wg.Done()
+				producerChan := make(chan bool, 1)
+				runChecksForCertificates(ip, nodeType, producerChan, numberOfNodes)
+			}()
+		}
+		go func() {
+			defer wg.Done()
+			validateNodeReachability(ip, nodeType, reportChan, numberOfNodes)
+		}()
+		go func() {
+			defer wg.Done()
+			runChecksForSystemResources(ip, nodeType, numberOfNodes)
+		}()
+		go func() {
+			defer wg.Done()
+			runChecksForSoftwareVersions(ip, nodeType, numberOfNodes)
+		}()
+	}
+	wg.Wait()
+	close(reportChan)
+	if <-doneChan {
+		fmt.Println("done!!")
+	}
+}
+
+func runChecksForCertificates(ipaddress string, nodeType string, doneChan chan bool, numberOfNodes *numberOfNodes) {
+	var successCount, failedCount int
+	result := validateCertificateFormat(ipaddress, "abc")
+	if !result.Valid {
+		failedCount++
+		result.Report.SummaryInfo.FailedCount = failedCount
+		chanWriter(reportChan, nodeType, result.Report, numberOfNodes)
+	} else {
+		result = validateCertificateExpiry(ipaddress, "abc")
+		if !result.Valid {
+			failedCount++
+			result.Report.SummaryInfo.FailedCount = failedCount
+			chanWriter(reportChan, nodeType, result.Report, numberOfNodes)
+		} else {
+			successCount++
+			report := report{
+				ipaddress:             ipaddress,
+				parameter:             "Certificates",
+				message:               "Certificate validation successful",
+				successfulChecksCount: successCount,
+			}
+			chanWriter(reportChan, nodeType, getReport(report, "Success"), numberOfNodes)
+		}
+	}
+	doneChan <- true
+}
+
+func runChecksForSystemResources(ipaddress string, nodeType string, numberOfNodes *numberOfNodes) {
+	var successCount, failedCount int
+	var errors []string
+	var resolutions []string
+	failed := false
+
+	result := validateFreeDisk(ipaddress, nodeType)
+	if !result.Valid {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		failed = true
+		failedCount++
+	} else if result.Valid && result.Report.StatusMessage.SubMessage != nil {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		successCount++
+	} else if result.Valid {
+		successCount++
+	}
+
+	result = validateCPU(ipaddress, nodeType)
+	if !result.Valid {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		failed = true
+		failedCount++
+	} else if result.Valid && result.Report.StatusMessage.SubMessage != nil {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		successCount++
+	} else if result.Valid {
+		successCount++
+	}
+
+	if failed {
+		report := report{
+			ipaddress:             ipaddress,
+			parameter:             "System Resources",
+			message:               "System Resource Check failed",
+			errors:                errors,
+			resolutions:           resolutions,
+			failedChecksCount:     failedCount,
+			successfulChecksCount: successCount,
+		}
+		chanWriter(reportChan, nodeType, getReport(report, "Failed"), numberOfNodes)
+
+	} else {
+		report := report{
+			ipaddress:             ipaddress,
+			parameter:             "System Resources",
+			message:               "All System Resources are as per the requirement",
+			errors:                errors,
+			resolutions:           resolutions,
+			failedChecksCount:     failedCount,
+			successfulChecksCount: successCount,
+		}
+		chanWriter(reportChan, nodeType, getReport(report, "Success"), numberOfNodes)
+	}
+}
+
+func runChecksForSoftwareVersions(ipaddress string, nodeType string, numberOfNodes *numberOfNodes) {
+	var successCount, failedCount int
+	var errors []string
+	var resolutions []string
+	failed := false
+
+	result := validateOSVersion(ipaddress, nodeType)
+	if !result.Valid {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		failed = true
+		failedCount++
+	} else if result.Valid && result.Report.StatusMessage.SubMessage != nil {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		successCount++
+	} else if result.Valid {
+		successCount++
+	}
+
+	result = validateLinuxCommands(ipaddress, nodeType)
+	if !result.Valid {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		failed = true
+		failedCount++
+	} else if result.Valid && result.Report.StatusMessage.SubMessage != nil {
+		errors = append(errors, result.Report.StatusMessage.SubMessage...)
+		resolutions = append(resolutions, result.Report.SummaryInfo.ToResolve...)
+		successCount++
+	} else if result.Valid {
+		successCount++
+	}
+
+	if failed {
+		report := report{
+			ipaddress:             ipaddress,
+			parameter:             result.Report.Parameter,
+			message:               result.Report.StatusMessage.MainMessage,
+			errors:                errors,
+			resolutions:           resolutions,
+			failedChecksCount:     failedCount,
+			successfulChecksCount: successCount,
+		}
+		chanWriter(reportChan, nodeType, getReport(report, "Failed"), numberOfNodes)
+
+	} else {
+		report := report{
+			ipaddress:             ipaddress,
+			parameter:             result.Report.Parameter,
+			message:               result.Report.StatusMessage.MainMessage,
+			errors:                errors,
+			resolutions:           resolutions,
+			failedChecksCount:     failedCount,
+			successfulChecksCount: successCount,
+		}
+		chanWriter(reportChan, nodeType, getReport(report, "Success"), numberOfNodes)
+	}
+}
+
+func getReport(testReport report, status string) reporting.Info {
+	return reporting.Info{
+		Hostip:    testReport.ipaddress,
+		Parameter: testReport.parameter,
+		Status:    status,
+		StatusMessage: &reporting.StatusMessage{
+			MainMessage: testReport.message,
+			SubMessage:  testReport.errors,
+		},
+		SummaryInfo: &reporting.SummaryInfo{
+			ToResolve:       testReport.resolutions,
+			SuccessfulCount: testReport.successfulChecksCount,
+			FailedCount:     testReport.failedChecksCount,
+		},
+	}
+}
+
+func startReportModule(nodeNumbers *numberOfNodes) {
+	wr := cli.NewWriter(os.Stdout, os.Stderr, os.Stdin)
+	tb := createTables(nodeNumbers)
+	rp := reporting.NewReportingModule(wr, tb)
+	go reporting.VerificationReports(reportChan, rp, nodeInfoMap, doneChan)
+}
+
+func createTables(nodeNumbers *numberOfNodes) map[string]*reporting.Table {
+	automateSummaryTableTitle := fmt.Sprintf("Summary: Automate <Nodes> - %d", nodeNumbers.numberOfAutomateNodes)
+	chefServerSummaryTableTitle := fmt.Sprintf("Summary: Chef Infra Server <Nodes> - %d", nodeNumbers.numberOfChefServerNodes)
+	postgresSQLSummaryTableTitle := fmt.Sprintf("Summary: PostgresSQL <Nodes> - %d", nodeNumbers.numberOfPostgresSQLNodes)
+	openSearchSummaryTableTitle := fmt.Sprintf("Summary: Opensearch <Nodes> - %d", nodeNumbers.numberOfOpenSearchNodes)
+	tb := make(map[string]*reporting.Table)
+	tb["AutomateStatusTable"] = getStatusTable("Automate")
+	tb["AutomateSummaryTable"] = getSummaryTable(automateSummaryTableTitle)
+	tb["ChefServerStatusTable"] = getStatusTable("Chef Infra Server")
+	tb["ChefServerSummaryTable"] = getSummaryTable(chefServerSummaryTableTitle)
+	tb["PostgresSQLStatusTable"] = getStatusTable("PostgresSQL")
+	tb["PostgresSQLSummaryTable"] = getSummaryTable(postgresSQLSummaryTableTitle)
+	tb["OpenSearchStatusTable"] = getStatusTable("OpenSearch")
+	tb["OpenSearchSummaryTable"] = getSummaryTable(openSearchSummaryTableTitle)
+	return tb
+}
+
+func getStatusTable(title string) *reporting.Table {
+	return &reporting.Table{
+		Title:     title,
+		Header:    table.Row{"No.", "Identifier", "Parameter", "Status", "Message"},
+		ColConfig: []table.ColumnConfig{{Number: 1, WidthMax: 5, WidthMin: 5}, {Number: 2, WidthMax: 15, WidthMin: 15}, {Number: 3, WidthMax: 25, WidthMin: 25}, {Number: 4, WidthMax: 15, WidthMin: 15}, {Number: 5, WidthMax: 60, WidthMin: 60}},
+	}
+}
+
+func getSummaryTable(title string) *reporting.Table {
+	return &reporting.Table{
+		Title:     title,
+		Header:    table.Row{"Parameter", "Successful", "Failed", "How to resolve it"},
+		ColConfig: []table.ColumnConfig{{Number: 1, WidthMax: 30, WidthMin: 30}, {Number: 2, WidthMax: 15, WidthMin: 15}, {Number: 3, WidthMax: 15, WidthMin: 15}, {Number: 4, WidthMax: 60, WidthMin: 60}},
+	}
+}
+
+func chanWriter(reportChan chan reporting.VerificationReport, nodeType string, report reporting.Info, nodeNumbers *numberOfNodes) {
+	total := 3 * nodeNumbers.numberOfAutomateNodes
+	if nodeType == "ChefServer" {
+		total = 3 * nodeNumbers.numberOfChefServerNodes
+	} else if nodeType == "OpenSearch" {
+		total = 4 * nodeNumbers.numberOfOpenSearchNodes
+	} else if nodeType == "PostgresSQL" {
+		total = 4 * nodeNumbers.numberOfPostgresSQLNodes
+	}
+	msg := reporting.VerificationReport{
+		TableKey:     nodeType,
+		Report:       report,
+		TotalReports: total,
+	}
+	reportChan <- msg
+}


Test patch:
diff --git a/components/automate-cli/cmd/chef-automate/pre_flight_check_test.go b/components/automate-cli/cmd/chef-automate/pre_flight_check_test.go
new file mode 100644
index 000000000..6dca5d553
--- /dev/null
+++ b/components/automate-cli/cmd/chef-automate/pre_flight_check_test.go
@@ -0,0 +1,151 @@
+package main
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+type ExpectedOutput struct {
+	SSHKeyFile           string
+	SSHPort              string
+	SSHUser              string
+	AutomatePrivateIps   []string
+	ChefServerPrivateIps []string
+}
+
+func TestGetInfraDetailsForDeploymentType(t *testing.T) {
+	type testCaseInfo struct {
+		testCaseDescription string
+		deploymentType      string
+		configPath          string
+		expectedOutput      ExpectedOutput
+		expectedError       bool
+	}
+
+	testCases := []testCaseInfo{
+		{
+			testCaseDescription: "should fetch infra details for existing-infra",
+			deploymentType:      EXISTING_INFRA_MODE,
+			configPath:          "../../pkg/testfiles/onprem/config.toml",
+			expectedOutput:      ExpectedOutput{SSHKeyFile: "/home/ec2-user/A2HA.pem", SSHPort: "22", SSHUser: "ec2-user", AutomatePrivateIps: []string{"192.0.2.0", "192.0.2.1"}, ChefServerPrivateIps: []string{"192.0.2.2"}},
+			expectedError:       false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.testCaseDescription, func(t *testing.T) {
+			infra, err := getInfraDetailsForDeploymentType(tc.deploymentType, tc.configPath)
+			assert.EqualValues(t, infra.Outputs.ChefServerPrivateIps.Value, tc.expectedOutput.ChefServerPrivateIps)
+			assert.EqualValues(t, infra.Outputs.AutomatePrivateIps.Value, tc.expectedOutput.AutomatePrivateIps)
+			assert.EqualValues(t, infra.Outputs.SSHKeyFile.Value, tc.expectedOutput.SSHKeyFile)
+			assert.EqualValues(t, infra.Outputs.SSHPort.Value, tc.expectedOutput.SSHPort)
+			assert.EqualValues(t, infra.Outputs.SSHUser.Value, tc.expectedOutput.SSHUser)
+			assert.Equal(t, err, nil)
+		})
+	}
+}
+
+func TestConstructAndGetNodeMap(t *testing.T) {
+	preflightCmdFlags.automate = true
+	var infra AutomteHAInfraDetails
+
+	type ExpectedOutput struct {
+		automate    bool
+		chef_server bool
+		frontend    bool
+	}
+
+	type testCaseInfo struct {
+		testCaseDescription string
+		automate            bool
+		chef_server         bool
+		frontend            bool
+		ExpectedOutput      ExpectedOutput
+	}
+
+	testCases := []testCaseInfo{
+		{
+			testCaseDescription: "should construct nodeMap for automate when automate flag is passed",
+			automate:            true,
+			chef_server:         false,
+			frontend:            false,
+			ExpectedOutput: ExpectedOutput{
+				automate:    true,
+				chef_server: false,
+				frontend:    false,
+			},
+		},
+		{
+			testCaseDescription: "should construct nodeMap for chef-server when chef-server flag is passed",
+			automate:            false,
+			chef_server:         true,
+			frontend:            false,
+			ExpectedOutput: ExpectedOutput{
+				automate:    false,
+				chef_server: true,
+				frontend:    false,
+			},
+		},
+		{
+			testCaseDescription: "should construct nodeMap for frontend when frontend flag is passed",
+			automate:            false,
+			chef_server:         false,
+			frontend:            true,
+			ExpectedOutput: ExpectedOutput{
+				automate:    false,
+				chef_server: false,
+				frontend:    true,
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.testCaseDescription, func(t *testing.T) {
+			preflightCmdFlags.automate = tc.automate
+			preflightCmdFlags.chef_server = tc.chef_server
+			preflightCmdFlags.frontend = tc.frontend
+
+			nodeMap := constructAndGetNodeMap(&infra)
+
+			assert.EqualValues(t, nodeMap.Automate.CmdInputs.NodeType, tc.ExpectedOutput.automate)
+			assert.EqualValues(t, nodeMap.ChefServer.CmdInputs.NodeType, tc.ExpectedOutput.chef_server)
+			assert.EqualValues(t, nodeMap.Frontend.CmdInputs.NodeType, tc.ExpectedOutput.frontend)
+		})
+	}
+}
+
+func TestShouldReturnErrorWhenHaDeploymentConfigFlagDonotHaveConfig(t *testing.T) {
+	preflightCmdFlags.haDeploymentConfig = true
+	err := runPreflightCheckCmd(nil, nil)
+	assert.Equal(t, err.Error(), "Config file should be passed with ha-deployment-config flag")
+}
+
+func TestShouldGetInfraDetails(t *testing.T) {
+	type testCaseInfo struct {
+		testCaseDescription string
+		configPath          string
+		expectedOutput      ExpectedOutput
+		expectedError       bool
+	}
+	testCases := []testCaseInfo{
+		{
+			testCaseDescription: "should fetch infra details",
+			configPath:          "../../pkg/testfiles/onprem/config.toml",
+			expectedOutput:      ExpectedOutput{SSHKeyFile: "/home/ec2-user/A2HA.pem", SSHPort: "22", SSHUser: "ec2-user", AutomatePrivateIps: []string{"192.0.2.0", "192.0.2.1"}, ChefServerPrivateIps: []string{"192.0.2.2"}},
+			expectedError:       false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.testCaseDescription, func(t *testing.T) {
+			infra, err := getInfraDetails(tc.configPath)
+			assert.EqualValues(t, infra.Outputs.ChefServerPrivateIps.Value, tc.expectedOutput.ChefServerPrivateIps)
+			assert.EqualValues(t, infra.Outputs.AutomatePrivateIps.Value, tc.expectedOutput.AutomatePrivateIps)
+			assert.EqualValues(t, infra.Outputs.SSHKeyFile.Value, tc.expectedOutput.SSHKeyFile)
+			assert.EqualValues(t, infra.Outputs.SSHPort.Value, tc.expectedOutput.SSHPort)
+			assert.EqualValues(t, infra.Outputs.SSHUser.Value, tc.expectedOutput.SSHUser)
+			assert.Equal(t, err, nil)
+		})
+	}
+}
diff --git a/components/automate-cli/cmd/chef-automate/verify_test.go b/components/automate-cli/cmd/chef-automate/verify_test.go
new file mode 100644
index 000000000..6cc2ee3f5
--- /dev/null
+++ b/components/automate-cli/cmd/chef-automate/verify_test.go
@@ -0,0 +1,908 @@
+package main
+
+import (
+	"testing"
+
+	verification "github.com/chef/automate/lib/verification"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	configFileName       = "config.toml"
+	parseConfigFileError = "Unable to parse config file"
+)
+
+func TestVerifyCmdFunc(t *testing.T) {
+	type args struct {
+		flagsObj *verifyCmdFlags
+	}
+	tests := []struct {
+		name string
+		args args
+	}{
+		{
+			name: "Test VerifyCmdFunc",
+			args: args{
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := verifyCmdFunc(tt.args.flagsObj)
+			err := got(&cobra.Command{}, []string{})
+			assert.Equal(t, nil, err)
+		})
+	}
+}
+
+func TestRunVerifyCmd(t *testing.T) {
+	type args struct {
+		cmd      *cobra.Command
+		args     []string
+		flagsObj *verifyCmdFlags
+	}
+	tests := []struct {
+		name              string
+		args              args
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+		mockFunc          verification.Verification
+		wantErr           bool
+		err               error
+	}{
+		{
+			name: "Test VerifyHaAWSProvision",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            true,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: true,
+			mockFunc: &verification.VerificationMock{
+				VerifyHAAWSProvisionFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyHaAWSManagedProvision",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     true,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: false,
+			mockFunc: &verification.VerificationMock{
+				VerifyHAAWSManagedProvisionFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyHaOnpremDeploy",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            true,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: false,
+			mockFunc: &verification.VerificationMock{
+				VerifyOnPremDeploymentFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyHaOnPremAWSManagedDeploy",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  true,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: false,
+			mockFunc: &verification.VerificationMock{
+				VerifyOnPremAWSManagedDeploymentFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyHaOnPremCustManagedDeploy",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{configFileName},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: true,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: false,
+			mockFunc: &verification.VerificationMock{
+				VerifyOnPremCustManagedDeploymentFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyHaAWSDeploy",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               true,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: true,
+			mockFunc: &verification.VerificationMock{
+				VerifyHAAWSDeploymentFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyHaAWSManagedDeploy",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        true,
+					standaloneDeploy:          false,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: true,
+			mockFunc: &verification.VerificationMock{
+				VerifyHAAWSManagedDeploymentFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: true,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyStandaloneDeploy",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          true,
+					certificates:              false,
+				},
+			},
+			A2HARBFileExist: false,
+			mockFunc: &verification.VerificationMock{
+				VerifyStandaloneDeploymentFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+		{
+			name: "Test VerifyCertificates",
+			args: args{
+				cmd:  &cobra.Command{},
+				args: []string{},
+				flagsObj: &verifyCmdFlags{
+					file:                      "",
+					haAWSProvision:            false,
+					haAWSManagedProvision:     false,
+					haOnpremDeploy:            false,
+					haOnPremAWSManagedDeploy:  false,
+					haOnPremCustManagedDeploy: false,
+					haAWSDeploy:               false,
+					haAWSManagedDeploy:        false,
+					standaloneDeploy:          false,
+					certificates:              true,
+				},
+			},
+			A2HARBFileExist: false,
+			mockFunc: &verification.VerificationMock{
+				VerifyCertificatesFunc: func(configFile string) error {
+					return nil
+				},
+			},
+			ManagedServicesOn: false,
+			wantErr:           false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := verifyCmdFlow{
+				Verification:      tt.mockFunc,
+				A2HARBFileExist:   tt.A2HARBFileExist,
+				ManagedServicesOn: tt.ManagedServicesOn,
+			}
+
+			err := v.runVerifyCmd(tt.args.cmd, tt.args.args, tt.args.flagsObj)
+
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyHaAWSProvision(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyHaAWSProvision: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSProvisionFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+		{
+			name: "Test VerifyHaAWSProvision: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSProvisionFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyHaAWSProvision(tt.args.configPath)
+
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyHaAWSManagedProvision(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyHaAWSManagedProvision: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSManagedProvisionFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+		{
+			name: "Test VerifyHaAWSManagedProvision: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSManagedProvisionFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyHaAWSManagedProvision(tt.args.configPath)
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyHaAWSDeploy(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyHaAWSDeploy: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   true,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+		{
+			name: "Test VerifyHaAWSDeploy: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSDeploymentFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   true,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+		{
+			name: "Test VerifyHaAWSDeploy: a2ha.rb file not present",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(errProvisonInfra),
+		},
+		{
+			name: "Test VerifyHaAWSDeploy: Managed services present",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: true,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New("This flag will not verify the Managed Services Setup. Please use the --ha-aws-managed-deploy flag."),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyHaAWSDeploy(tt.args.configPath)
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyHaAWSManagedDeploy(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyHaAWSManagedDeploy: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSManagedDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   true,
+				ManagedServicesOn: true,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+		{
+			name: "Test VerifyHaAWSManagedDeploy: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSManagedDeploymentFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   true,
+				ManagedServicesOn: true,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+		{
+			name: "Test VerifyHaAWSManagedDeploy: a2ha.rb file not present",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSManagedDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: true,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(errProvisonInfra),
+		},
+		{
+			name: "Test VerifyHaAWSManagedDeploy: Managed services not present",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyHAAWSManagedDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New("Managed Services flag is not set. Cannot verify the config."),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyHaAWSManagedDeploy(tt.args.configPath)
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyStandaloneDeploy(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyStandaloneDeploy: check a2ha.rb file",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyStandaloneDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   true,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New("Deployment type does not match with the requested flag."),
+		},
+		{
+			name: "Test VerifyStandaloneDeploy: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyStandaloneDeploymentFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+		{
+			name: "Test VerifyStandaloneDeploy: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyStandaloneDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyStandaloneDeploy(tt.args.configPath)
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyHaOnpremDeploy(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyHaOnpremDeploy: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyOnPremDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+		{
+			name: "Test VerifyHaOnpremDeploy: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyOnPremDeploymentFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyHaOnpremDeploy(tt.args.configPath)
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyHaOnPremAWSManagedDeploy(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyHaOnPremAWSManagedDeploy: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyOnPremAWSManagedDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+		{
+			name: "Test VerifyHaOnPremAWSManagedDeploy: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyOnPremAWSManagedDeploymentFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyHaOnPremAWSManagedDeploy(tt.args.configPath)
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
+
+func TestVerifyHaOnPremCustManagedDeploy(t *testing.T) {
+	type fields struct {
+		Verification      verification.Verification
+		A2HARBFileExist   bool
+		ManagedServicesOn bool
+	}
+	type args struct {
+		configPath string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Test VerifyHaOnPremCustManagedDeploy: verification successful",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyOnPremCustManagedDeploymentFunc: func(configFile string) error {
+						return nil
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: false,
+		},
+		{
+			name: "Test VerifyHaOnPremCustManagedDeploy: verification failed",
+			fields: fields{
+				Verification: &verification.VerificationMock{
+					VerifyOnPremCustManagedDeploymentFunc: func(configFile string) error {
+						return errors.New(parseConfigFileError)
+					},
+				},
+				A2HARBFileExist:   false,
+				ManagedServicesOn: false,
+			},
+			args:    args{configPath: configFileName},
+			wantErr: true,
+			err:     errors.New(parseConfigFileError),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			v := &verifyCmdFlow{
+				Verification:      tt.fields.Verification,
+				A2HARBFileExist:   tt.fields.A2HARBFileExist,
+				ManagedServicesOn: tt.fields.ManagedServicesOn,
+			}
+			err := v.verifyHaOnPremCustManagedDeploy(tt.args.configPath)
+			if tt.wantErr {
+				assert.EqualError(t, err, tt.err.Error())
+			} else {
+				assert.Equal(t, nil, err)
+			}
+		})
+	}
+}
diff --git a/lib/config_parser/parser_test.go b/lib/config_parser/parser_test.go
new file mode 100644
index 000000000..8a78c3bf3
--- /dev/null
+++ b/lib/config_parser/parser_test.go
@@ -0,0 +1,250 @@
+package config_parser
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	sc "github.com/chef/automate/api/config/deployment"
+	w "github.com/chef/automate/api/config/shared/wrappers"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParseAWSAutomateConfig(t *testing.T) {
+	type args struct {
+		configFile string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *HAAwsConfigToml
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Parse AWS Config successful",
+			args: args{configFile: "./testdata/AWSConfig.toml"},
+			want: &HAAwsConfigToml{Architecture: struct {
+				ConfigInitials struct {
+					SecretsKeyFile              string "toml:\"secrets_key_file\""
+					SecretsStoreFile            string "toml:\"secrets_store_file\""
+					Architecture                string "toml:\"architecture\""
+					WorkspacePath               string "toml:\"workspace_path\""
+					SSHUser                     string "toml:\"ssh_user\""
+					SSHKeyFile                  string "toml:\"ssh_key_file\""
+					SSHPort                     string "toml:\"ssh_port\""
+					SudoPassword                string "toml:\"sudo_password\""
+					LoggingMonitoringManagement string "toml:\"logging_monitoring_management\""
+					NewElk                      string "toml:\"new_elk\""
+					ExistingElkInstanceIP       string "toml:\"existing_elk_instance_ip\""
+					ExistingElkPort             string "toml:\"existing_elk_port\""
+					ExistingElkCert             string "toml:\"existing_elk_cert\""
+					ExistingElkUsername         string "toml:\"existing_elk_username\""
+					ExistingElkPassword         string "toml:\"existing_elk_password\""
+					BackupMount                 string "toml:\"backup_mount\""
+					BackupConfig                string "toml:\"backup_config\""
+					S3BucketName                string "toml:\"s3_bucketName\""
+					HabitatUIDGid               string "toml:\"habitat_uid_gid\""
+				} "toml:\"aws\""
+			}{
+				ConfigInitials: struct {
+					SecretsKeyFile              string "toml:\"secrets_key_file\""
+					SecretsStoreFile            string "toml:\"secrets_store_file\""
+					Architecture                string "toml:\"architecture\""
+					WorkspacePath               string "toml:\"workspace_path\""
+					SSHUser                     string "toml:\"ssh_user\""
+					SSHKeyFile                  string "toml:\"ssh_key_file\""
+					SSHPort                     string "toml:\"ssh_port\""
+					SudoPassword                string "toml:\"sudo_password\""
+					LoggingMonitoringManagement string "toml:\"logging_monitoring_management\""
+					NewElk                      string "toml:\"new_elk\""
+					ExistingElkInstanceIP       string "toml:\"existing_elk_instance_ip\""
+					ExistingElkPort             string "toml:\"existing_elk_port\""
+					ExistingElkCert             string "toml:\"existing_elk_cert\""
+					ExistingElkUsername         string "toml:\"existing_elk_username\""
+					ExistingElkPassword         string "toml:\"existing_elk_password\""
+					BackupMount                 string "toml:\"backup_mount\""
+					BackupConfig                string "toml:\"backup_config\""
+					S3BucketName                string "toml:\"s3_bucketName\""
+					HabitatUIDGid               string "toml:\"habitat_uid_gid\""
+				}{
+					SecretsKeyFile:   "/hab/a2_deploy_workspace/secrets.key",
+					SecretsStoreFile: "/hab/a2_deploy_workspace/secrets.json",
+					Architecture:     "aws",
+					WorkspacePath:    "/hab/a2_deploy_workspace",
+					BackupMount:      "/mnt/automate_backups",
+				},
+			}},
+			wantErr: false,
+			err:     nil,
+		},
+		{
+			name:    "Parse AWs Config file not found",
+			args:    args{configFile: "AWSConfig.toml"},
+			want:    nil,
+			wantErr: true,
+			err:     errors.New("error in reading config toml file: open AWSConfig.toml: no such file or directory"),
+		},
+		{
+			name:    "Parse AWs Config failed",
+			args:    args{configFile: "./testdata/AWSConfigFailure.toml"},
+			want:    nil,
+			wantErr: true,
+			err:     errors.New("error in unmarshalling config toml file: (30, 21): unescaped control character U+000A"),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cp := &ConfigParserImpl{}
+			got, err := cp.ParseAWSAutomateConfig(tt.args.configFile)
+			if tt.wantErr {
+				assert.Equal(t, tt.err.Error(), err.Error())
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ConfigParserImpl.ParseAWSAutomateConfig() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestParseOnPremConfig(t *testing.T) {
+	type args struct {
+		configFile string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *HAOnPremConfigToml
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Parse OnPrem Config successful",
+			args: args{configFile: "./testdata/OnPremConfig.toml"},
+			want: &HAOnPremConfigToml{Architecture: struct {
+				ConfigInitials struct {
+					SecretsKeyFile              string "toml:\"secrets_key_file,omitempty\""
+					SecretsStoreFile            string "toml:\"secrets_store_file,omitempty\""
+					Architecture                string "toml:\"architecture,omitempty\""
+					WorkspacePath               string "toml:\"workspace_path,omitempty\""
+					SSHUser                     string "toml:\"ssh_user,omitempty\""
+					SSHKeyFile                  string "toml:\"ssh_key_file,omitempty\""
+					SSHPort                     string "toml:\"ssh_port,omitempty\""
+					SudoPassword                string "toml:\"sudo_password,omitempty\""
+					LoggingMonitoringManagement string "toml:\"logging_monitoring_management,omitempty\""
+					NewElk                      string "toml:\"new_elk,omitempty\""
+					ExistingElkInstanceIP       string "toml:\"existing_elk_instance_ip,omitempty\""
+					ExistingElkPort             string "toml:\"existing_elk_port,omitempty\""
+					ExistingElkCert             string "toml:\"existing_elk_cert,omitempty\""
+					ExistingElkUsername         string "toml:\"existing_elk_username,omitempty\""
+					ExistingElkPassword         string "toml:\"existing_elk_password,omitempty\""
+					BackupMount                 string "toml:\"backup_mount,omitempty\""
+					HabitatUIDGid               string "toml:\"habitat_uid_gid,omitempty\""
+					BackupConfig                string "toml:\"backup_config,omitempty\""
+				} "toml:\"existing_infra\""
+			}{
+				ConfigInitials: struct {
+					SecretsKeyFile              string "toml:\"secrets_key_file,omitempty\""
+					SecretsStoreFile            string "toml:\"secrets_store_file,omitempty\""
+					Architecture                string "toml:\"architecture,omitempty\""
+					WorkspacePath               string "toml:\"workspace_path,omitempty\""
+					SSHUser                     string "toml:\"ssh_user,omitempty\""
+					SSHKeyFile                  string "toml:\"ssh_key_file,omitempty\""
+					SSHPort                     string "toml:\"ssh_port,omitempty\""
+					SudoPassword                string "toml:\"sudo_password,omitempty\""
+					LoggingMonitoringManagement string "toml:\"logging_monitoring_management,omitempty\""
+					NewElk                      string "toml:\"new_elk,omitempty\""
+					ExistingElkInstanceIP       string "toml:\"existing_elk_instance_ip,omitempty\""
+					ExistingElkPort             string "toml:\"existing_elk_port,omitempty\""
+					ExistingElkCert             string "toml:\"existing_elk_cert,omitempty\""
+					ExistingElkUsername         string "toml:\"existing_elk_username,omitempty\""
+					ExistingElkPassword         string "toml:\"existing_elk_password,omitempty\""
+					BackupMount                 string "toml:\"backup_mount,omitempty\""
+					HabitatUIDGid               string "toml:\"habitat_uid_gid,omitempty\""
+					BackupConfig                string "toml:\"backup_config,omitempty\""
+				}{
+					SecretsKeyFile:   "/hab/a2_deploy_workspace/secrets.key",
+					SecretsStoreFile: "/hab/a2_deploy_workspace/secrets.json",
+					Architecture:     "existing_nodes",
+					WorkspacePath:    "/hab/a2_deploy_workspace",
+					SSHPort:          "22",
+					BackupMount:      "/mnt/automate_backups",
+				},
+			}},
+			wantErr: false,
+			err:     nil,
+		},
+		{
+			name:    "Parse OnPrem Config file not found",
+			args:    args{configFile: "OnPremConfig.toml"},
+			want:    nil,
+			wantErr: true,
+			err:     errors.New("error in reading config toml file: open OnPremConfig.toml: no such file or directory"),
+		},
+		{
+			name:    "Parse OnPrem Config failed",
+			args:    args{configFile: "./testdata/OnPremConfigFailure.toml"},
+			want:    nil,
+			wantErr: true,
+			err:     errors.New("error in unmarshalling config toml file: (10, 21): unescaped control character U+000A"),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cp := &ConfigParserImpl{}
+			got, err := cp.ParseOnPremConfig(tt.args.configFile)
+			if tt.wantErr {
+				assert.Equal(t, tt.err.Error(), err.Error())
+			}
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ConfigParserImpl.ParseOnPremConfig() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestParseStandaloneConfig(t *testing.T) {
+	Cfg := sc.NewAutomateConfig()
+	Cfg.Global.V1.Fqdn = w.String("Public_DNS_name")
+	type args struct {
+		configFile string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    *sc.AutomateConfig
+		wantErr bool
+		err     error
+	}{
+		{
+			name: "Parse Standalone Config successful",
+			args: args{
+				configFile: "./testdata/StandaloneConfig.toml",
+			},
+			want:    Cfg,
+			wantErr: false,
+		},
+		{
+			name: "Parse Standalone Config failed",
+			args: args{
+				configFile: "StandaloneConfig.toml",
+			},
+			want:    &sc.AutomateConfig{},
+			wantErr: true,
+			err:     errors.New("Failed to read config file located at StandaloneConfig.toml: open StandaloneConfig.toml: no such file or directory"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cp := &ConfigParserImpl{}
+			got, err := cp.ParseStandaloneConfig(tt.args.configFile)
+			if tt.wantErr {
+				assert.Equal(t, tt.err.Error(), err.Error())
+			} else {
+				assert.Equal(t, tt.want.Global.V1.Fqdn.GetValue(), got.Global.V1.Fqdn.GetValue())
+			}
+
+		})
+	}
+}
diff --git a/lib/config_parser/testdata/AWSConfig.toml b/lib/config_parser/testdata/AWSConfig.toml
new file mode 100644
index 000000000..a644d7720
--- /dev/null
+++ b/lib/config_parser/testdata/AWSConfig.toml
@@ -0,0 +1,37 @@
+# This is a Chef Automate AWS HA mode configuration file. You can run
+# 'chef-automate deploy' with this config file and it should
+# successfully create a new Chef Automate HA instances with default settings.
+
+[architecture.aws]
+## === INPUT NEEDED ===
+# Eg.: ssh_user = "ubuntu"
+ssh_user = ""
+
+# custome ssh port no to connect instances, default will be 22
+# Eg.: ssh_port = "22"
+ssh_port = ""
+
+# Private SSH key file path, which has access to all the instances.
+# Eg.: ssh_key_file = "~/.ssh/A2HA.pem"
+ssh_key_file = ""
+
+# Eg.: backup_config = "efs" or "s3"
+backup_config = ""
+
+#    Note: 1. To create new bucket, make sure to give a unique name
+#          2. User can also add existing bucket 
+#
+# If s3 is selected for backup_config,
+#     uncomment and add the value for following 's3_bucketName' attribute
+
+# s3_bucketName = ""
+## === ===
+
+secrets_key_file = "/hab/a2_deploy_workspace/secrets.key"
+secrets_store_file = "/hab/a2_deploy_workspace/secrets.json"
+architecture = "aws"
+workspace_path = "/hab/a2_deploy_workspace"
+
+# DON'T MODIFY THE BELOW LINE (backup_mount)
+backup_mount = "/mnt/automate_backups"
+
diff --git a/lib/config_parser/testdata/AWSConfigFailure.toml b/lib/config_parser/testdata/AWSConfigFailure.toml
new file mode 100644
index 000000000..d619a0c3f
--- /dev/null
+++ b/lib/config_parser/testdata/AWSConfigFailure.toml
@@ -0,0 +1,37 @@
+# This is a Chef Automate AWS HA mode configuration file. You can run
+# 'chef-automate deploy' with this config file and it should
+# successfully create a new Chef Automate HA instances with default settings.
+
+[architecture.aws]
+## === INPUT NEEDED ===
+# Eg.: ssh_user = "ubuntu"
+ssh_user = ""
+
+# custome ssh port no to connect instances, default will be 22
+# Eg.: ssh_port = "22"
+ssh_port = ""
+
+# Private SSH key file path, which has access to all the instances.
+# Eg.: ssh_key_file = "~/.ssh/A2HA.pem"
+ssh_key_file = ""
+
+# Eg.: backup_config = "efs" or "s3"
+backup_config = ""
+
+#    Note: 1. To create new bucket, make sure to give a unique name
+#          2. User can also add existing bucket 
+#
+# If s3 is selected for backup_config,
+#     uncomment and add the value for following 's3_bucketName' attribute
+
+# s3_bucketName = ""
+## === ===
+
+secrets_key_file = "/hab/a2_deploy_workspace/secrets.key
+secrets_store_file = "/hab/a2_deploy_workspace/secrets.json"
+architecture = "aws"
+workspace_path = "/hab/a2_deploy_workspace"
+
+# DON'T MODIFY THE BELOW LINE (backup_mount)
+backup_mount = "/mnt/automate_backups"
+
diff --git a/lib/config_parser/testdata/OnPremConfig.toml b/lib/config_parser/testdata/OnPremConfig.toml
new file mode 100644
index 000000000..c641c1a18
--- /dev/null
+++ b/lib/config_parser/testdata/OnPremConfig.toml
@@ -0,0 +1,15 @@
+# This is a Chef Automate on-prem HA mode configuration file. You can run
+# 'chef-automate deploy' with this config file, and it should
+# successfully create a new Chef Automate HA instance with default settings.
+[architecture.existing_infra]
+ssh_user = ""
+# private ssh key file path to access instances
+# Eg.: ssh_user = "~/.ssh/A2HA.pem"
+ssh_key_file = ""
+ssh_port = "22"
+secrets_key_file = "/hab/a2_deploy_workspace/secrets.key"
+secrets_store_file = "/hab/a2_deploy_workspace/secrets.json"
+architecture = "existing_nodes"
+# DON'T MODIFY THE BELOW LINE (workspace_path)
+workspace_path = "/hab/a2_deploy_workspace"
+backup_mount = "/mnt/automate_backups"
diff --git a/lib/config_parser/testdata/OnPremConfigFailure.toml b/lib/config_parser/testdata/OnPremConfigFailure.toml
new file mode 100644
index 000000000..8d34f84b8
--- /dev/null
+++ b/lib/config_parser/testdata/OnPremConfigFailure.toml
@@ -0,0 +1,15 @@
+# This is a Chef Automate on-prem HA mode configuration file. You can run
+# 'chef-automate deploy' with this config file, and it should
+# successfully create a new Chef Automate HA instance with default settings.
+[architecture.existing_infra]
+ssh_user = ""
+# private ssh key file path to access instances
+# Eg.: ssh_user = "~/.ssh/A2HA.pem"
+ssh_key_file = ""
+ssh_port = "22"
+secrets_key_file = "/hab/a2_deploy_workspace/secrets.key
+secrets_store_file = "/hab/a2_deploy_workspace/secrets.json"
+architecture = "existing_nodes"
+# DON'T MODIFY THE BELOW LINE (workspace_path)
+workspace_path = "/hab/a2_deploy_workspace"
+backup_mount = "/mnt/automate_backups"
diff --git a/lib/config_parser/testdata/StandaloneConfig.toml b/lib/config_parser/testdata/StandaloneConfig.toml
new file mode 100644
index 000000000..4097767d3
--- /dev/null
+++ b/lib/config_parser/testdata/StandaloneConfig.toml
@@ -0,0 +1,6 @@
+# This is a default Chef Automate configuration file. You can run
+# 'chef-automate deploy' with this config file and it should
+# successfully create a new Chef Automate instance with default settings.
+
+[global.v1]
+fqdn = "Public_DNS_name"
diff --git a/lib/reporting/helperForVerification_test.go b/lib/reporting/helperForVerification_test.go
new file mode 100644
index 000000000..a03e4597a
--- /dev/null
+++ b/lib/reporting/helperForVerification_test.go
@@ -0,0 +1,410 @@
+package reporting
+
+import (
+	"testing"
+
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/fatih/color"
+	"github.com/jedib0t/go-pretty/v5/table"
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	automate     = "Automate"
+	certificate  = "Certificates"
+	toResolveMsg = "1. Add the start and end certificate markers"
+	summaryTable = "AutomateSummaryTable"
+	ip           = "127.0.0.4"
+	ipResult     = "127.0.0.4:"
+)
+
+var tbMap = map[string]*Table{
+	automate: {
+		Title:     automate,
+		Header:    table.Row{"No.", "Identifier", "Parameter", "Status", "Message"},
+		ColConfig: []table.ColumnConfig{{Number: 1, WidthMax: 5, WidthMin: 5}, {Number: 2, WidthMax: 15, WidthMin: 15}, {Number: 3, WidthMax: 25, WidthMin: 25}, {Number: 4, WidthMax: 15, WidthMin: 15}, {Number: 5, WidthMax: 60, WidthMin: 60}},
+	},
+	summaryTable: {
+		Title:     "SUMMARY : Automate",
+		Header:    table.Row{"Parameter", "Successful", "Failed", "How to resolve it"},
+		ColConfig: []table.ColumnConfig{{Number: 1, WidthMax: 30, WidthMin: 30}, {Number: 2, WidthMax: 15, WidthMin: 15}, {Number: 3, WidthMax: 15, WidthMin: 15}, {Number: 4, WidthMax: 65, WidthMin: 65}},
+	},
+}
+
+func getMockReportingModule(wr *cli.Writer) Reporting {
+	return &ReportingModule{
+		writer: wr,
+		tables: tbMap,
+	}
+}
+
+var reportChan = make(chan VerificationReport, 10)
+var nodeInfoMap = make(map[string][]Info)
+var done = make(chan bool, 1)
+
+func TestVerificationReports(t *testing.T) {
+	cw := getMockWriterImpl()
+	type args struct {
+		reportChan  chan VerificationReport
+		reporting   Reporting
+		nodeInfoMap map[string][]Info
+		done        chan bool
+	}
+	test := struct {
+		name string
+		args args
+		want string
+	}{
+
+		name: successStr,
+		args: args{
+			reportChan:  reportChan,
+			reporting:   getMockReportingModule(cw.CliWriter),
+			nodeInfoMap: nodeInfoMap,
+			done:        done,
+		},
+		want: "Automate\n+-------+-----------------+---------------------------+-----------------+--------------------------------------------------------------+\n|   NO. |      IDENTIFIER |                 PARAMETER |          STATUS |                                                      MESSAGE |\n+-------+-----------------+---------------------------+-----------------+--------------------------------------------------------------+\n+-------+-----------------+---------------------------+-----------------+--------------------------------------------------------------+\nSUMMARY : Automate\n+--------------------------------+-----------------+-----------------+-------------------------------------------------------------------+\n|                      PARAMETER |      SUCCESSFUL |          FAILED |                                                 HOW TO RESOLVE IT |\n+--------------------------------+-----------------+-----------------+-------------------------------------------------------------------+\n+--------------------------------+-----------------+-----------------+-------------------------------------------------------------------+\n",
+	}
+
+	report := VerificationReport{
+		TableKey: "",
+		Report: Info{
+			Hostip:        "",
+			Parameter:     "",
+			Status:        "",
+			StatusMessage: &StatusMessage{},
+			SummaryInfo:   &SummaryInfo{},
+		},
+		TotalReports: 0,
+	}
+
+	reportChan <- report
+	reportChan <- report
+	close(reportChan)
+
+	VerificationReports(test.args.reportChan, test.args.reporting, test.args.nodeInfoMap, test.args.done)
+	if <-done {
+		assert.Equal(t, test.want, cw.Output())
+
+	}
+}
+
+func TestCreateStatusTableRows(t *testing.T) {
+	type args struct {
+		index     int
+		rowData   *Info
+		reporting Reporting
+	}
+	tests := []struct {
+		name string
+		args args
+		want []table.Row
+	}{
+		{
+			name: "Status is Success",
+			args: args{
+				index: 1,
+				rowData: &Info{
+					Hostip:    ip,
+					Parameter: certificate,
+					Status:    successStr,
+					StatusMessage: &StatusMessage{
+						MainMessage: "Cerificate validation successful",
+						SubMessage:  []string{"Certificate about to expire <Date>"},
+					},
+					SummaryInfo: &SummaryInfo{},
+				},
+				reporting: getMockReportingModule(getMockWriterImpl().CliWriter),
+			},
+			want: []table.Row{{1, ip, certificate, color.New(color.FgGreen).Sprint("✔ Success"), "Cerificate validation successful"}, {"", "", "", "", color.New(color.FgYellow).Sprint("! [ Warning ] ") + "Certificate about to expire <Date>"}},
+		},
+		{
+			name: "Status is Failed",
+			args: args{
+				index: 1,
+				rowData: &Info{
+					Hostip:    ip,
+					Parameter: certificate,
+					Status:    failedStr,
+					StatusMessage: &StatusMessage{
+						MainMessage: "Certificate validation failed",
+						SubMessage:  []string{"Certificate is not formatted properly"},
+					},
+					SummaryInfo: &SummaryInfo{},
+				},
+				reporting: getMockReportingModule(getMockWriterImpl().CliWriter),
+			},
+			want: []table.Row{{1, color.New(color.FgRed).Sprint(ip), color.New(color.FgRed).Sprint(certificate), color.New(color.FgRed).Sprint("✖ Failed"), color.New(color.FgRed).Sprint("Certificate validation failed")}, {"", "", "", "", color.New(color.FgRed).Sprint("✖ [ Failed ] Certificate is not formatted properly")}},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := createStatusTableRows(tt.args.index, tt.args.rowData, tt.args.reporting)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func TestCreateSummaryTableRowData(t *testing.T) {
+	type args struct {
+		summary map[string]SummaryInfo
+		rowData *Info
+	}
+	tests := []struct {
+		name string
+		args args
+		want map[string]SummaryInfo
+	}{
+		{
+			name: successStr,
+			args: args{
+				summary: map[string]SummaryInfo{},
+				rowData: &Info{
+					Hostip:    ip,
+					Parameter: certificate,
+					Status:    failedStr,
+					StatusMessage: &StatusMessage{
+						MainMessage: "Certificate B validation failed",
+						SubMessage:  []string{"Certificate is not formatted properly"},
+					},
+					SummaryInfo: &SummaryInfo{
+						FailedCount: 1,
+						ToResolve:   []string{"Add the start and end certificate markers"},
+					},
+				},
+			},
+			want: map[string]SummaryInfo{
+				certificate: {
+					SuccessfulCount: 0,
+					FailedCount:     1,
+					ToResolve:       []string{ipResult, toResolveMsg},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := createSummaryTableRowData(tt.args.summary, tt.args.rowData)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func TestCreateSummaryTableRows(t *testing.T) {
+	type args struct {
+		rowData   map[string]SummaryInfo
+		reporting Reporting
+	}
+	tests := []struct {
+		name string
+		args args
+		want []table.Row
+	}{
+		{
+			name: "ToResolve steps are present",
+			args: args{
+				rowData: map[string]SummaryInfo{
+					certificate: {
+						SuccessfulCount: 0,
+						FailedCount:     1,
+						ToResolve:       []string{ipResult, toResolveMsg},
+					},
+				},
+				reporting: getMockReportingModule(getMockWriterImpl().CliWriter),
+			},
+			want: []table.Row{{"Certificates", color.New(color.FgGreen).Sprint("0"), color.New(color.FgRed).Sprint("1"), ipResult}, {"", "", "", toResolveMsg}},
+		},
+		{
+			name: "ToResolve steps are not present",
+			args: args{
+				rowData: map[string]SummaryInfo{
+					certificate: {
+						SuccessfulCount: 1,
+						FailedCount:     0,
+						ToResolve:       []string{},
+					},
+				},
+				reporting: getMockReportingModule(getMockWriterImpl().CliWriter),
+			},
+			want: []table.Row{{"Certificates", color.New(color.FgGreen).Sprint("1"), color.New(color.FgRed).Sprint("0"), ""}},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := createSummaryTableRows(tt.args.rowData, tt.args.reporting)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func TestShowProgress(t *testing.T) {
+	type args struct {
+		tableProgess map[string]progress
+		lines        int
+	}
+	test := struct {
+		name string
+		args args
+		want int
+	}{
+		name: successStr,
+		args: args{
+			tableProgess: map[string]progress{
+				automate: {
+					currentCount: 2,
+					totalCount:   3,
+				},
+			},
+			lines: 1,
+		},
+		want: 1,
+	}
+
+	got := showProgress(test.args.tableProgess, test.args.lines)
+	assert.Equal(t, test.want, got)
+
+}
+
+func TestUpdateTableTitle(t *testing.T) {
+	type args struct {
+		reporting Reporting
+		nodeInfo  map[string][]Info
+		keyMap    map[string][]string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "Title updated correctly when status is failed",
+			args: args{
+				reporting: getMockReportingModule(getMockWriterImpl().CliWriter),
+				nodeInfo: map[string][]Info{
+					automate: {
+						{
+							Hostip:    ip,
+							Parameter: certificate,
+							Status:    failedStr,
+						},
+					},
+				},
+				keyMap: map[string][]string{
+					automate: {automate, summaryTable},
+				},
+			},
+			want: color.New(color.FgRed).Sprint("✖ Automate"),
+		},
+		{
+			name: "Title updated correctly when status is succes",
+			args: args{
+				reporting: getMockReportingModule(getMockWriterImpl().CliWriter),
+				nodeInfo: map[string][]Info{
+					automate: {
+						{
+							Hostip:    ip,
+							Parameter: certificate,
+							Status:    successStr,
+						},
+					},
+				},
+				keyMap: map[string][]string{
+					automate: {automate, summaryTable},
+				},
+			},
+			want: color.New(color.FgGreen).Sprint("✔ Automate"),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			oldTitle := tt.args.reporting.GetTable(tt.args.keyMap[automate][0]).Title
+			updateTableTitle(tt.args.reporting, tt.args.nodeInfo, tt.args.keyMap)
+			assert.Equal(t, tt.want, tt.args.reporting.GetTable(tt.args.keyMap[automate][0]).Title)
+			tt.args.reporting.GetTable(tt.args.keyMap[automate][0]).Title = oldTitle
+		})
+	}
+}
+
+func TestCheckFailedStatus(t *testing.T) {
+	type args struct {
+		info []Info
+	}
+	tests := []struct {
+		name string
+		args args
+		want bool
+	}{
+		{
+			name: "Info array contains report with failed status",
+			args: args{
+				info: []Info{
+					{Status: failedStr},
+					{Status: successStr},
+					{Status: successStr},
+				},
+			},
+			want: true,
+		},
+		{
+			name: "Info array does not contains report with failed status",
+			args: args{
+				info: []Info{
+					{Status: successStr},
+					{Status: successStr},
+					{Status: successStr},
+				},
+			},
+			want: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := checkFailedStatus(tt.args.info); got != tt.want {
+				t.Errorf("checkFailedStatus() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestMatchNodeNameWithRespectiveTablesName(t *testing.T) {
+	type args struct {
+		substring string
+		keys      []string
+	}
+	test := struct {
+		name string
+		args args
+		want []string
+	}{
+		name: "Keys containing substring in alphabetical order",
+		args: args{
+			substring: "ab",
+			keys:      []string{"abab", "abc", "bcd", "bcda"},
+		},
+		want: []string{"abab", "abc"},
+	}
+
+	got := matchNodeNameWithRespectiveTablesName(test.args.substring, test.args.keys)
+	assert.Equal(t, test.want, got)
+}
+
+func TestCreateIndexForToResolve(t *testing.T) {
+	type args struct {
+		data []string
+	}
+	test := struct {
+		name string
+		args args
+		want []string
+	}{
+		name: successStr,
+		args: args{
+			data: []string{"Step 1", "Step 2"},
+		},
+		want: []string{"1. Step 1", "2. Step 2"},
+	}
+
+	got := createIndexForToResolve(test.args.data)
+	assert.Equal(t, test.want, got)
+
+}
diff --git a/lib/reporting/reporting_test.go b/lib/reporting/reporting_test.go
new file mode 100644
index 000000000..8eb54ace0
--- /dev/null
+++ b/lib/reporting/reporting_test.go
@@ -0,0 +1,367 @@
+package reporting
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/chef/automate/lib/majorupgrade_utils"
+	"github.com/fatih/color"
+	"github.com/jedib0t/go-pretty/v5/table"
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	successStr = "Success"
+	failedStr  = "Failed"
+	waringStr  = "Warning"
+	test1      = "test1"
+	test2      = "test2"
+	header     = "header"
+	header1    = "header1"
+	header2    = "header2"
+	col        = "col"
+	col1       = "col1"
+	col2       = "col2"
+)
+
+type fields struct {
+	writer *cli.Writer
+	tables map[string]*Table
+}
+
+var tableMap = map[string]*Table{
+	test1: {
+		Title:     test1,
+		Rows:      []table.Row{{col}},
+		Header:    table.Row{header},
+		Footer:    table.Row{},
+		ColConfig: []table.ColumnConfig{},
+	},
+	test2: {
+		Title:     test2,
+		Rows:      []table.Row{{col1, col2}},
+		Header:    table.Row{header1, header2},
+		Footer:    table.Row{},
+		ColConfig: []table.ColumnConfig{},
+	},
+}
+
+var module = fields{
+	writer: getMockWriterImpl().CliWriter,
+	tables: tableMap,
+}
+
+func TestNewReportingModule(t *testing.T) {
+	tests := []struct {
+		name string
+		args fields
+		want Reporting
+	}{
+		{
+			name: successStr,
+			args: module,
+			want: nil,
+		},
+	}
+	for _, tt := range tests {
+		NewReportingModule(tt.args.writer, tt.args.tables)
+	}
+}
+
+func TestGetTable(t *testing.T) {
+	tests := []struct {
+		name   string
+		fields fields
+		args   string
+		want   *Table
+	}{
+		{
+			name:   successStr,
+			fields: module,
+			args:   test1,
+			want: &Table{
+				Title:     test1,
+				Rows:      []table.Row{{col}},
+				Header:    table.Row{header},
+				Footer:    table.Row{},
+				ColConfig: []table.ColumnConfig{},
+			},
+		},
+	}
+	for _, tt := range tests {
+		r := &ReportingModule{
+			writer: tt.fields.writer,
+			tables: tt.fields.tables,
+		}
+		got := r.GetTable(tt.args)
+		assert.Equal(t, tt.want, got)
+	}
+}
+
+func TestGetAllTables(t *testing.T) {
+	tests := []struct {
+		name   string
+		fields fields
+		want   map[string]*Table
+	}{
+		{
+			name:   successStr,
+			fields: module,
+			want: map[string]*Table{
+				test1: {
+					Title:     test1,
+					Rows:      []table.Row{{col}},
+					Header:    table.Row{header},
+					Footer:    table.Row{},
+					ColConfig: []table.ColumnConfig{},
+				},
+				test2: {
+					Title:     test2,
+					Rows:      []table.Row{{col1, col2}},
+					Header:    table.Row{header1, header2},
+					Footer:    table.Row{},
+					ColConfig: []table.ColumnConfig{},
+				}},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := &ReportingModule{
+				writer: tt.fields.writer,
+				tables: tt.fields.tables,
+			}
+			if got := r.GetAllTables(); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ReportingModule.GetAllTables() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestGetAllTableKeys(t *testing.T) {
+	test := struct {
+		name   string
+		fields fields
+		want   []string
+	}{
+
+		name:   successStr,
+		fields: module,
+		want:   []string{test1, test2},
+	}
+
+	r := &ReportingModule{
+		writer: test.fields.writer,
+		tables: test.fields.tables,
+	}
+	got := r.GetAllTableKeys()
+	assert.Equal(t, test.want, got)
+
+}
+
+func TestSetTable(t *testing.T) {
+	testTableMap := map[string]*Table{
+		test1: {
+			Title:     test1,
+			Rows:      []table.Row{{col}},
+			Header:    table.Row{header},
+			Footer:    table.Row{},
+			ColConfig: []table.ColumnConfig{},
+		},
+		test2: {
+			Title:     test2,
+			Rows:      []table.Row{{col1, col2}},
+			Header:    table.Row{header1, header2},
+			Footer:    table.Row{},
+			ColConfig: []table.ColumnConfig{},
+		},
+	}
+	type args struct {
+		key   string
+		table *Table
+	}
+	test := struct {
+		name   string
+		fields fields
+		args   args
+		want   *Table
+	}{
+		name: "Replace old table with given table for given key",
+		fields: fields{
+			writer: getMockWriterImpl().CliWriter,
+			tables: testTableMap,
+		},
+		args: args{
+			key:   test1,
+			table: testTableMap[test2],
+		},
+		want: testTableMap[test2],
+	}
+
+	r := &ReportingModule{
+		writer: test.fields.writer,
+		tables: test.fields.tables,
+	}
+	r.SetTable(test.args.key, test.args.table)
+	assert.EqualValues(t, test.want, r.GetTable(test1))
+}
+
+func TestAppendSpecialCharater(t *testing.T) {
+	type args struct {
+		symbol int
+		str    string
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		args   args
+		want   string
+	}{
+		{
+			name:   "Append x",
+			fields: module,
+			args: args{
+				symbol: Failed,
+				str:    failedStr,
+			},
+			want: "✖ Failed",
+		},
+		{
+			name:   "Append ✔",
+			fields: module,
+			args: args{
+				symbol: Success,
+				str:    successStr,
+			},
+			want: "✔ Success",
+		},
+		{
+			name:   "Append !",
+			fields: module,
+			args: args{
+				symbol: Warning,
+				str:    waringStr,
+			},
+			want: "! Warning",
+		},
+		{
+			name:   "Add [] bracket around text",
+			fields: module,
+			args: args{
+				symbol: SquareBracket,
+				str:    waringStr,
+			},
+			want: "[ Warning ] ",
+		},
+		{
+			name:   "String returned as it is",
+			fields: module,
+			args: args{
+				symbol: 4,
+				str:    waringStr,
+			},
+			want: waringStr,
+		},
+	}
+	for _, tt := range tests {
+		r := &ReportingModule{
+			writer: tt.fields.writer,
+			tables: tt.fields.tables,
+		}
+		got := r.AppendSpecialCharater(tt.args.symbol, tt.args.str)
+		assert.Equal(t, tt.want, got)
+	}
+}
+
+func TestChangeColour(t *testing.T) {
+	type args struct {
+		fgColor int
+		msg     string
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		args   args
+		want   string
+	}{
+		{
+			name:   "Change color of string to red",
+			fields: module,
+			args: args{
+				fgColor: Red,
+				msg:     test1,
+			},
+			want: color.New(color.FgRed).Sprint(test1),
+		},
+		{
+			name:   "Change color of string to green",
+			fields: module,
+			args: args{
+				fgColor: Green,
+				msg:     test1,
+			},
+			want: color.New(color.FgGreen).Sprint(test1),
+		},
+		{
+			name:   "Change color of string to yellow",
+			fields: module,
+			args: args{
+				fgColor: Yellow,
+				msg:     test1,
+			},
+			want: color.New(color.FgYellow).Sprint(test1),
+		},
+		{
+			name:   "Returned string is same as input",
+			fields: module,
+			args: args{
+				fgColor: 4,
+				msg:     test1,
+			},
+			want: test1,
+		},
+	}
+	for _, tt := range tests {
+		r := &ReportingModule{
+			writer: tt.fields.writer,
+			tables: tt.fields.tables,
+		}
+		got := r.ChangeColour(tt.args.fgColor, tt.args.msg)
+		assert.Equal(t, tt.want, got)
+	}
+}
+
+func TestGenerateTableOutputAndPrint(t *testing.T) {
+	cw := getMockWriterImpl()
+	type args struct {
+		tb *Table
+	}
+
+	test := struct {
+		name   string
+		fields fields
+		args   args
+		want   string
+	}{
+		name: "Print  given table",
+		fields: fields{
+			writer: cw.CliWriter,
+			tables: tableMap,
+		},
+		args: args{
+			tb: tableMap[test1],
+		},
+		want: "test1\n+--------+\n| HEADER |\n+--------+\n| col    |\n+--------+\n",
+	}
+
+	r := &ReportingModule{
+		writer: test.fields.writer,
+		tables: test.fields.tables,
+	}
+	r.GenerateTableOutputAndPrint(test.args.tb)
+	assert.Equal(t, test.want, cw.Output())
+}
+
+func getMockWriterImpl() *majorupgrade_utils.CustomWriter {
+	return majorupgrade_utils.NewCustomWriter()
+}