Repository: chef/automate
Pull request #7961 (2023-06-13 09:35:40)

Create CLI Verify subcommand which validates given Config toml file before Deployment
### :nut_and_bolt: Description: What code changed, and why?
<!-- /!\ Please ensure that you are NOT disclosing any customer information without their consent /!\ -->
Create CLI Verify subcommand which validates the given Config toml file before Deployment.

### :chains: Related Resources
https://chefio.atlassian.net/browse/CHEF-2053
### :+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
https://progresssoftware.sharepoint.com/:v:/s/ChefCoreC/EbT_ck2d6XJBkgGb6r-Eh_ABvRHq3-iEFn1YcBq4KI92-A?e=pxn6C7

![Screenshot 2023-06-19 at 1 33 49 PM](https://github.com/chef/automate/assets/11033984/579722b0-7f34-4270-9fff-b691f9c370c0)

![Screenshot 2023-06-19 at 3 25 07 PM](https://github.com/chef/automate/assets/11033984/2934ab68-8af4-4602-ba06-9ccdd4494981)


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


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

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

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

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![25.2%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '25.2%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 25.2% Coverage on New Code (is less than 80%)  

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![39.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '39.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 39.1% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![39.5%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '39.5%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 39.5% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![39.5%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '39.5%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 39.5% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![40.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/40-16px.png '40.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 40.9% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![40.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/40-16px.png '40.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 40.9% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![40.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/40-16px.png '40.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 40.9% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![40.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/40-16px.png '40.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 40.9% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![75.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '75.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 75.8% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![76.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '76.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 76.1% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![76.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '76.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 76.1% Coverage on New Code (is less than 80%)  

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


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

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

[![Failed condition](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/FailedConditionIcon-16px.png 'Failed condition')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) [![75.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '75.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7961) 75.8% Coverage on New Code (is less than 80%)  

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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



Patch:
diff --git a/.bldr.toml b/.bldr.toml
index d6876584d..7344be58a 100644
--- a/.bldr.toml
+++ b/.bldr.toml
@@ -260,6 +260,7 @@ paths = [
   "lib/httputils/*",
   "lib/io/*",
   "lib/license/*",
+  "lib/logger/*",
   "lib/platform/command/*",
   "lib/platform/sys/*",
   "lib/proc/*",
@@ -443,7 +444,6 @@ paths = [
   "lib/cereal/*",
   "lib/certgenerateutils/*",
   "lib/config/*",
-  "lib/config_parser/*",
   "lib/db/*",
   "lib/executil/*",
   "lib/grpc/*",
@@ -458,7 +458,6 @@ paths = [
   "lib/proc/*",
   "lib/product/*",
   "lib/proxy/*",
-  "lib/reporting/*",
   "lib/secrets/*",
   "lib/sshutils/*",
   "lib/stringutils/*",
@@ -468,7 +467,6 @@ paths = [
   "lib/user/*",
   "lib/userutils/*",
   "lib/uuid4/*",
-  "lib/verification/*",
   "lib/version/*"
 ]
 
@@ -558,6 +556,7 @@ paths = [
   "lib/httputils/*",
   "lib/io/*",
   "lib/license/*",
+  "lib/logger/*",
   "lib/majorupgrade_utils/*",
   "lib/platform/command/*",
   "lib/platform/config/*",
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
index e8edb802c..e7735fe2c 100644
--- a/components/automate-cli/cmd/chef-automate/verify.go
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -1,15 +1,28 @@
 package main
 
 import (
+	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 	"strconv"
+	"strings"
+	"time"
 
 	"github.com/chef/automate/components/automate-cli/pkg/docs"
-	"github.com/chef/automate/components/automate-cli/pkg/status"
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/response"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/server"
 	"github.com/chef/automate/components/automate-cli/pkg/verifysystemdcreate"
-	verification "github.com/chef/automate/lib/verification"
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/httputils"
+	"github.com/chef/automate/lib/logger"
+	"github.com/chef/automate/lib/platform/command"
+	"github.com/chef/automate/lib/sshutils"
+	"github.com/chef/automate/lib/stringutils"
+	"github.com/chef/automate/lib/version"
 	"github.com/spf13/cobra"
 )
 
@@ -17,26 +30,41 @@ const (
 	VERIFY_SERVER_PORT        = "VERIFY_SERVER_PORT"
 	BINARY_DESTINATION_FOLDER = "/etc/automate-verify"
 	SYSTEMD_PATH              = "/etc/systemd/system"
+	REMOTE_NODES              = "remote nodes"
+	BASTION                   = "bastion"
+	SLEEP_DURATION            = 2 * time.Second
+)
+
+var (
+	statusAPIEndpoint     = fmt.Sprintf("http://localhost:%s/status", getPort())
+	batchCheckAPIEndpoint = fmt.Sprintf("http://localhost:%s/api/v1/checks/batch-checks", getPort())
 )
 
 type verifyCmdFlags struct {
-	file                      string
-	haAWSProvision            bool
-	haAWSManagedProvision     bool
-	haOnpremDeploy            bool
-	haOnPremAWSManagedDeploy  bool
-	haOnPremCustManagedDeploy bool
-	haAWSDeploy               bool
-	haAWSManagedDeploy        bool
-	standaloneDeploy          bool
-	certificates              bool
-	debug                     bool
+	config      string
+	debug       bool
+	prettyPrint bool
 }
 
 type verifyCmdFlow struct {
-	Verification      verification.Verification
-	A2HARBFileExist   bool
-	ManagedServicesOn bool
+	Client               httputils.HTTPClient
+	CreateSystemdService verifysystemdcreate.CreateSystemdService
+	SystemdCreateUtils   verifysystemdcreate.SystemdCreateUtils
+	Config               *config.HaDeployConfig
+	SSHUtil              sshutils.SSHUtil
+	Writer               *cli.Writer
+	prettyPrint          bool
+}
+
+func NewVerifyCmdFlow(client httputils.HTTPClient, createSystemdService verifysystemdcreate.CreateSystemdService, systemdCreateUtils verifysystemdcreate.SystemdCreateUtils, config *config.HaDeployConfig, sshutil sshutils.SSHUtil, writer *cli.Writer) *verifyCmdFlow {
+	return &verifyCmdFlow{
+		Client:               client,
+		CreateSystemdService: createSystemdService,
+		SystemdCreateUtils:   systemdCreateUtils,
+		Config:               config,
+		SSHUtil:              sshutil,
+		Writer:               writer,
+	}
 }
 
 type verifyServeCmdFlow struct{}
@@ -87,69 +115,40 @@ func init() {
 		RunE: verifySystemdCreateFunc(&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",
+	verifyCmd.PersistentFlags().StringVarP(
+		&flagsObj.config,
+		"config",
+		"c",
 		"",
 		"Config file that needs to be verified")
+
+	verifyCmd.Flags().BoolVarP(
+		&flagsObj.debug,
+		"debug",
+		"d",
+		false,
+		"enable debugging for verify command")
+
+	verifyCmd.Flags().BoolVarP(
+		&flagsObj.prettyPrint,
+		"pretty-print",
+		"p",
+		false,
+		"pretty-print json response")
+
 	verifyServeCmd.Flags().BoolVarP(
 		&flagsObj.debug,
 		"debug",
 		"d",
 		false,
-		"enable debugging")
+		"enable debugging for verify serve command")
+
 	verifySystemdServiceCreateCmd.Flags().BoolVarP(
 		&flagsObj.debug,
 		"debug",
 		"d",
 		false,
-		"enable debugging")
+		"enable debugging for verify systemd-service create command")
 
 	verifySystemdServiceCmd.AddCommand(verifySystemdServiceCreateCmd)
 	verifyCmd.AddCommand(verifySystemdServiceCmd)
@@ -165,13 +164,7 @@ func verifyServeCmdFunc(flagsObj *verifyCmdFlags) func(cmd *cobra.Command, args
 }
 
 func (v *verifyServeCmdFlow) runVerifyServeCmd(cmd *cobra.Command, args []string, debug bool) error {
-	port := server.DEFAULT_PORT
-	env_port := os.Getenv(VERIFY_SERVER_PORT)
-	if env_port != "" {
-		if _, err := strconv.Atoi(env_port); err == nil {
-			port = env_port
-		}
-	}
+	port := getPort()
 	writer.Println("Using port " + port)
 	vs, err := server.NewVerifyServer(port, debug)
 	if err != nil {
@@ -188,7 +181,6 @@ func verifySystemdCreateFunc(flagsObj *verifyCmdFlags) func(cmd *cobra.Command,
 }
 
 func (v *verifySystemdCreateFlow) runVerifySystemdCreateCmd(cmd *cobra.Command, args []string, debug bool) error {
-
 	createSystemdServiceWithBinary, err := verifysystemdcreate.NewCreateSystemdService(
 		verifysystemdcreate.NewSystemdCreateUtilsImpl(),
 		BINARY_DESTINATION_FOLDER,
@@ -204,126 +196,286 @@ func (v *verifySystemdCreateFlow) runVerifySystemdCreateCmd(cmd *cobra.Command,
 
 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(),
+		defaultLevel := "info"
+		if flagsObj.debug {
+			defaultLevel = "debug"
 		}
+
+		log, err := logger.NewLogger("text", defaultLevel)
+		if err != nil {
+			return err
+		}
+
+		createSystemdServiceWithBinary, err := verifysystemdcreate.NewCreateSystemdService(
+			verifysystemdcreate.NewSystemdCreateUtilsImpl(),
+			BINARY_DESTINATION_FOLDER,
+			SYSTEMD_PATH,
+			flagsObj.debug,
+			writer,
+		)
+		if err != nil {
+			return err
+		}
+
+		c := NewVerifyCmdFlow(httputils.NewClient(log), createSystemdServiceWithBinary, verifysystemdcreate.NewSystemdCreateUtilsImpl(), config.NewHaDeployConfig(), sshutils.NewSSHUtilWithCommandExecutor(sshutils.NewSshClient(), log, command.NewExecExecutor()), writer)
 		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]
+	var configPath string
+
+	// TODO : config flag is optional for now. Need to handle the default config path
+	if len(strings.TrimSpace(flagsObj.config)) > 0 {
+		configPath = flagsObj.config
 	}
 
-	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)
+	v.prettyPrint = flagsObj.prettyPrint
+
+	err := v.Config.ParseAndVerify(configPath)
+	if err != nil {
 		return err
-	case flagsObj.haOnPremAWSManagedDeploy:
-		err := v.verifyHaOnPremAWSManagedDeploy(configPath)
+	}
+
+	// Get config required for batch-check API call
+	batchCheckConfig := &models.Config{}
+	err = batchCheckConfig.PopulateWith(v.Config)
+	if err != nil {
 		return err
-	case flagsObj.haOnPremCustManagedDeploy:
-		err := v.verifyHaOnPremCustManagedDeploy(configPath)
+	}
+
+	// TODO: For now just print the result as json. Need to merge the result with the response from batch-check API call for remote.
+
+	err = v.runVerifyServiceForBastion(*batchCheckConfig)
+	if err != nil {
 		return err
-	case flagsObj.certificates:
-		err := v.Verification.VerifyCertificates("")
+	}
+
+	err = v.runVerifyServiceForRemote(*batchCheckConfig)
+	if err != nil {
 		return err
-	default:
-		fmt.Println(cmd.UsageString())
-		return nil
 	}
 
+	return nil
 }
 
-func (v *verifyCmdFlow) verifyHaAWSProvision(configPath string) error {
-	if err := v.Verification.VerifyHAAWSProvision(configPath); err != nil {
-		return status.Annotate(err, status.ConfigError)
+// Runs automate-verify service on bastion
+func (v *verifyCmdFlow) runVerifyServiceForBastion(batchCheckConfig models.Config) error {
+	v.Writer.Println("Checking automate-verify service on bastion")
+
+	//Call status API to check if automate-verify service is running on bastion
+	_, responseBody, err := v.Client.MakeRequest(http.MethodGet, statusAPIEndpoint, nil)
+	if err != nil {
+		v.Writer.Printf("error while checking automate-verify service on bastion: %v\n", err)
+		if responseBody != nil {
+			v.Writer.Printf("Error response body: %s\n", string(responseBody))
+		}
+		v.Writer.Println("Adding automate-verify service to systemd and starting the service")
+
+		// create systemd service with current CLI binary
+		err = v.createSystemdOnBastion()
+		if err != nil {
+			return err
+		}
+
+		v.Writer.Println("Added automate-verify service to systemd and started the the service")
+	} else {
+		resultBytes, err := v.getResultFromResponseBody(responseBody)
+		if err != nil {
+			return err
+		}
+
+		var result models.StatusDetails
+
+		if err := json.Unmarshal(resultBytes, &result); err != nil {
+			return fmt.Errorf("failed to unmarshal Result field: %v", err)
+		}
+
+		// Upgrade if current CLI is latest version than the existing CLI on bastion
+		latestCLIVersion := version.BuildTime
+		if latestCLIVersion > result.CliVersion {
+			v.Writer.Printf("Upgrading from CLI %s to latest %s on bastion\n", result.CliVersion, latestCLIVersion)
+			// create systemd service with current CLI binary
+			err = v.createSystemdOnBastion()
+			if err != nil {
+				return err
+			}
+			v.Writer.Println("Upgrading from CLI completed on bastion")
+		}
 	}
+	v.Writer.Println("Checking automate-verify service on bastion completed")
 
-	return nil
+	// Doing batch-check API call for bastion
+	batchCheckBastionReq := models.BatchCheckRequest{
+		Checks: constants.GetBastionChecks(),
+		Config: batchCheckConfig,
+	}
+
+	return v.makeBatchCheckAPICall(batchCheckBastionReq, BASTION)
 }
 
-func (v *verifyCmdFlow) verifyHaAWSManagedProvision(configPath string) error {
-	if err := v.Verification.VerifyHAAWSManagedProvision(configPath); err != nil {
-		return status.Annotate(err, status.ConfigError)
+// Runs automate-verify service on remote nodes
+func (v *verifyCmdFlow) runVerifyServiceForRemote(batchCheckConfig models.Config) error {
+
+	// TODO: Need to check if automate-verify service is already running on remote nodes and upgrade if needed.
+	var port, keyFile, userName string
+	var hostIPs []string
+
+	if v.Config.IsExistingInfra() {
+		port, keyFile, userName = v.getSSHConfig(v.Config.Architecture.ExistingInfra)
+		hostIPs = v.getHostIPs(
+			v.Config.ExistingInfra.Config.AutomatePrivateIps,
+			v.Config.ExistingInfra.Config.ChefServerPrivateIps,
+			v.Config.ExistingInfra.Config.PostgresqlPrivateIps,
+			v.Config.ExistingInfra.Config.OpensearchPrivateIps,
+		)
+
+		sshConfig := sshutils.NewSshConfig("", port, keyFile, userName)
+
+		destFileName := "chef-automate"
+
+		// Copying CLI binary to remote nodes
+		err := v.copyCLIOnRemoteNodes(destFileName, sshConfig, hostIPs)
+		if err != nil {
+			return err
+		}
+
+		// Starting automate-verify service on remote nodes
+		err = v.startServiceOnRemoteNodes(destFileName, sshConfig, hostIPs)
+		if err != nil {
+			return err
+		}
 	}
 
-	return nil
+	//TODO: Need to handle the case for AWS
+
+	// Doing batch-check API call for remote nodes
+	batchCheckRemoteReq := models.BatchCheckRequest{
+		Checks: constants.GetRemoteChecks(),
+		Config: batchCheckConfig,
+	}
+	return v.makeBatchCheckAPICall(batchCheckRemoteReq, REMOTE_NODES)
 }
 
-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)
+// Creates systemd service with current CLI binary
+func (v *verifyCmdFlow) createSystemdOnBastion() error {
+	err := v.CreateSystemdService.Create()
+	if err != nil {
+		return err
 	}
-	return status.New(status.InvalidCommandArgsError, "This flag will not verify the Managed Services Setup. Please use the --ha-aws-managed-deploy flag.")
+	// TODO: Added 2 seconds sleep to make sure service is started. Need to handle this in a better way.
+	time.Sleep(SLEEP_DURATION)
+	return nil
 }
 
-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
+// Makes batch-check API call
+func (v *verifyCmdFlow) makeBatchCheckAPICall(requestBody models.BatchCheckRequest, nodeType string) error {
+	v.Writer.Printf("Doing batch-check API call for %s\n", nodeType)
+	_, responseBody, err := v.Client.MakeRequest(http.MethodPost, batchCheckAPIEndpoint, requestBody)
+	if err != nil {
+		if responseBody != nil {
+			v.Writer.Printf("Error response body: %s\n", string(responseBody))
 		}
-		return status.New(status.InvalidCommandArgsError, errProvisonInfra)
+		return fmt.Errorf("error while doing batch-check API call for %s: %v", nodeType, err)
+	} else {
+		resultBytes, err := v.getResultFromResponseBody(responseBody)
+		if err != nil {
+			return err
+		}
+		v.Writer.Printf("Response for batch-check API on %s: \n%s\n", nodeType, string(resultBytes))
 	}
-	return status.New(status.InvalidCommandArgsError, "Managed Services flag is not set. Cannot verify the config.")
+	v.Writer.Printf("Batch-check API call for %s completed\n", nodeType)
+	return nil
 }
 
-func (v *verifyCmdFlow) verifyStandaloneDeploy(configPath string) error {
-	if v.A2HARBFileExist {
-		return status.New(status.InvalidCommandArgsError, "Deployment type does not match with the requested flag.")
+// Copies CLI binary to remote nodes
+func (v *verifyCmdFlow) copyCLIOnRemoteNodes(destFileName string, sshConfig sshutils.SSHConfig, hostIPs []string) error {
+	v.Writer.Println("Copying automate-verify CLI to remote nodes")
+	currentBinaryPath, err := v.SystemdCreateUtils.GetBinaryPath()
+	if err != nil {
+		return err
+	}
+	copyResults := v.SSHUtil.CopyFileToRemoteConcurrently(sshConfig, currentBinaryPath, destFileName, false, hostIPs)
+	isError := false
+	for _, result := range copyResults {
+		if result.Error != nil {
+			v.Writer.Errorf("Remote copying automate-verify CLI failed on node : %s with error: %v\n", result.HostIP, result.Error)
+			isError = true
+		}
 	}
-	if err := v.Verification.VerifyStandaloneDeployment(configPath); err != nil {
-		return status.Annotate(err, status.ConfigError)
+	if isError {
+		return fmt.Errorf("remote copying failed")
 	}
-
+	v.Writer.Println("Copying automate-verify CLI to remote nodes completed")
 	return nil
 }
 
-func (v *verifyCmdFlow) verifyHaOnpremDeploy(configPath string) error {
-	if err := v.Verification.VerifyOnPremDeployment(configPath); err != nil {
-		return status.Annotate(err, status.ConfigError)
+// Starts automate-verify service on remote nodes
+func (v *verifyCmdFlow) startServiceOnRemoteNodes(destFileName string, sshConfig sshutils.SSHConfig, hostIPs []string) error {
+	v.Writer.Println("Creating automate-verify service on remote nodes")
+	cmd := fmt.Sprintf("sudo /tmp/%s verify systemd-service create", destFileName)
+	excuteResults := v.SSHUtil.ExecuteConcurrently(sshConfig, cmd, hostIPs)
+	isError := false
+	for _, result := range excuteResults {
+		if result.Error != nil {
+			v.Writer.Errorf("Remote execution of cmd '%s' automate-verify CLI failed on node : %s with error: %v\n", cmd, result.HostIP, result.Error)
+			isError = true
+		}
+	}
+	if isError {
+		return fmt.Errorf("remote execution failed")
 	}
+	// TODO: Added 2 seconds sleep to make sure service is started. Need to handle this in a better way.
+	time.Sleep(SLEEP_DURATION)
+	v.Writer.Println("Created automate-verify service on remote nodes")
 	return nil
 }
 
-func (v *verifyCmdFlow) verifyHaOnPremAWSManagedDeploy(configPath string) error {
-	if err := v.Verification.VerifyOnPremAWSManagedDeployment(configPath); err != nil {
-		return status.Annotate(err, status.ConfigError)
+// Returns result field from response body
+func (v *verifyCmdFlow) getResultFromResponseBody(responseBody []byte) ([]byte, error) {
+	var responseBodyStruct response.ResponseBody
+	if err := json.Unmarshal(responseBody, &responseBodyStruct); err != nil {
+		return nil, fmt.Errorf("failed to unmarshal response body: %v", err)
 	}
-	return nil
+
+	var resultBytes []byte
+	var err error
+	if v.prettyPrint {
+		resultBytes, err = json.MarshalIndent(responseBodyStruct.Result, "", "    ")
+	} else {
+		resultBytes, err = json.Marshal(responseBodyStruct.Result)
+	}
+
+	if err != nil {
+		return nil, fmt.Errorf("failed to marshal result field: %v", err)
+	}
+
+	return resultBytes, nil
 }
 
-func (v *verifyCmdFlow) verifyHaOnPremCustManagedDeploy(configPath string) error {
-	if err := v.Verification.VerifyOnPremCustManagedDeployment(configPath); err != nil {
-		return status.Annotate(err, status.ConfigError)
+// Returns port, keyFile, userName
+func (v *verifyCmdFlow) getSSHConfig(config *config.ConfigInitials) (string, string, string) {
+	return config.SSHPort, config.SSHKeyFile, config.SSHUser
+}
+
+// Returns concatenated list of automatePrivateIps, chefServerPrivateIps, postgresqlPrivateIps, opensearchPrivateIps
+func (v *verifyCmdFlow) getHostIPs(automatePrivateIps, chefServerPrivateIps, postgresqlPrivateIps, opensearchPrivateIps []string) []string {
+	var hostIPs []string
+	hostIPs = stringutils.ConcatSlice(hostIPs, automatePrivateIps)
+	hostIPs = stringutils.ConcatSlice(hostIPs, chefServerPrivateIps)
+	hostIPs = stringutils.ConcatSlice(hostIPs, postgresqlPrivateIps)
+	hostIPs = stringutils.ConcatSlice(hostIPs, opensearchPrivateIps)
+	return hostIPs
+}
+
+// Returns port
+func getPort() string {
+	port := server.DEFAULT_PORT
+	envPort := os.Getenv(VERIFY_SERVER_PORT)
+	if envPort != "" {
+		if _, err := strconv.Atoi(envPort); err == nil {
+			port = envPort
+		}
 	}
-	return nil
+	return port
 }
diff --git a/components/automate-cli/pkg/verifyserver/models/status.go b/components/automate-cli/pkg/verifyserver/models/status.go
index ebc5e422f..3fcc60e72 100644
--- a/components/automate-cli/pkg/verifyserver/models/status.go
+++ b/components/automate-cli/pkg/verifyserver/models/status.go
@@ -7,9 +7,10 @@ type ServiceDetails struct {
 }
 
 type StatusDetails struct {
-	Status   string            `json:"status"`
-	Services *[]ServiceDetails `json:"services"`
-	Error    string            `json:"error"`
+	Status     string            `json:"status"`
+	Services   *[]ServiceDetails `json:"services"`
+	CliVersion string            `json:"cli_version"`
+	Error      string            `json:"error"`
 }
 
 type StatusApiResponse struct {
diff --git a/components/automate-cli/pkg/verifyserver/server/api/v1/status.go b/components/automate-cli/pkg/verifyserver/server/api/v1/status.go
index c035598c4..8d304da1a 100644
--- a/components/automate-cli/pkg/verifyserver/server/api/v1/status.go
+++ b/components/automate-cli/pkg/verifyserver/server/api/v1/status.go
@@ -4,6 +4,7 @@ import (
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/response"
+	"github.com/chef/automate/lib/version"
 	"github.com/gofiber/fiber/v2"
 )
 
@@ -11,14 +12,16 @@ func (h *Handler) GetStatus(c *fiber.Ctx) error {
 	services, err := h.StatusService.GetServices()
 	if err != nil {
 		return c.JSON(response.BuildSuccessResponse(&models.StatusDetails{
-			Status:   constants.OK,
-			Services: &[]models.ServiceDetails{},
-			Error:    err.Error(),
+			Status:     constants.OK,
+			Services:   &[]models.ServiceDetails{},
+			CliVersion: version.BuildTime,
+			Error:      err.Error(),
 		}))
 	}
 
 	return c.JSON(response.BuildSuccessResponse(&models.StatusDetails{
-		Status:   constants.OK,
-		Services: services,
+		Status:     constants.OK,
+		Services:   services,
+		CliVersion: version.BuildTime,
 	}))
 }
diff --git a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
index 793b14e2c..b629a7860 100644
--- a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
+++ b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc.go
@@ -40,7 +40,7 @@ type systemdInput struct {
 	ServiceCommand     string
 }
 
-type CreateSystemdService struct {
+type CreateSystemdServiceImpl struct {
 	SystemdCreateUtils      SystemdCreateUtils
 	BinaryDestinationFolder string
 	SystemdLocation         string
@@ -49,12 +49,23 @@ type CreateSystemdService struct {
 	Writer                  *cli.Writer
 }
 
+type CreateSystemdService interface {
+	Create() error
+}
+type MockCreateSystemdService struct {
+	CreateFun func() error
+}
+
+func (m *MockCreateSystemdService) Create() error {
+	return m.CreateFun()
+}
+
 func NewCreateSystemdService(
 	systemdCreateUtils SystemdCreateUtils,
 	binaryDestinationFolder string,
 	systemdLocation string,
 	debug bool,
-	writer *cli.Writer) (*CreateSystemdService, error) {
+	writer *cli.Writer) (CreateSystemdService, error) {
 	level := INFO_LEVEL
 	if debug {
 		level = DEBUG_LEVEL
@@ -75,7 +86,7 @@ func NewCreateSystemdService(
 	if systemdLocation == "" {
 		return nil, errors.New("Systemd location cannot be empty")
 	}
-	return &CreateSystemdService{
+	return &CreateSystemdServiceImpl{
 		SystemdCreateUtils:      systemdCreateUtils,
 		BinaryDestinationFolder: binaryDestinationFolder,
 		SystemdLocation:         systemdLocation,
@@ -85,7 +96,7 @@ func NewCreateSystemdService(
 }
 
 // Create the systemd service file.
-func (css *CreateSystemdService) createSystemdServiceFile() error {
+func (css *CreateSystemdServiceImpl) createSystemdServiceFile() error {
 	// Create the template.
 	tmpl, err := template.New(TEMPLATE_NAME).Parse(TEMPLATE_TEXT)
 	if err != nil {
@@ -121,8 +132,24 @@ func (css *CreateSystemdService) createSystemdServiceFile() error {
 	return nil
 }
 
+// Check if status is active and then stop the service.
+func (css *CreateSystemdServiceImpl) stopIfSystemdServiceIsActive() error {
+	service := fmt.Sprintf(SYSTEMD_FILE, SERVICE_NAME)
+	err := css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"status", service})
+	if err == nil {
+		css.Logger.Debugln("Status of service is active")
+		err = css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"stop", service})
+		if err != nil {
+			return errors.Wrap(err, "Error stopping service")
+		}
+		css.Logger.Debugln("Stopped the service successfully")
+	}
+	css.Logger.Debugln("Checked status of service successfully")
+	return nil
+}
+
 // Enable and start the systemd service.
-func (css *CreateSystemdService) enableSystemdService() error {
+func (css *CreateSystemdServiceImpl) enableSystemdService() error {
 	service := fmt.Sprintf(SYSTEMD_FILE, SERVICE_NAME)
 	err := css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"daemon-reload"})
 	if err != nil {
@@ -142,12 +169,13 @@ func (css *CreateSystemdService) enableSystemdService() error {
 	return nil
 }
 
-func (css *CreateSystemdService) isSystemdEnabled() error {
+func (css *CreateSystemdServiceImpl) isSystemdEnabled() error {
 	return css.SystemdCreateUtils.ExecuteShellCommand("systemctl", []string{"is-enabled", fmt.Sprintf(SYSTEMD_FILE, SERVICE_NAME)})
 }
 
-// Create and start the systemd service.
-func (css *CreateSystemdService) Create() error {
+// Create and start the systemd service for automate-verify if not exist.
+// Else replace the existing service and restart it.
+func (css *CreateSystemdServiceImpl) Create() error {
 
 	currentBinaryPath, err := css.SystemdCreateUtils.GetBinaryPath()
 	css.Logger.Debugf("Current binary path %s", currentBinaryPath)
@@ -160,9 +188,17 @@ func (css *CreateSystemdService) Create() error {
 	}
 	css.Logger.Debugln("Systemd is found on this system")
 
+	//If service is already running, stop it and then create it again.
 	err = css.isSystemdEnabled()
 	if err == nil {
-		return errors.New(fmt.Sprintf("Service %[1]v already exists on this node, use systemctl start/stop/status %[1]v", SERVICE_NAME))
+		err = css.stopIfSystemdServiceIsActive()
+		if err != nil {
+			return err
+		}
+		err = css.enableSystemdService()
+		if err != nil {
+			return err
+		}
 	}
 	css.Logger.Debugf("Service %s is not running on this system", SERVICE_NAME)
 
@@ -173,13 +209,18 @@ func (css *CreateSystemdService) Create() error {
 		return err
 	}
 
-	css.Writer.Printf("Binary copied to %s\n", fullBinaryDestination)
+	css.Logger.Debugf("Binary copied from %s to %s\n", currentBinaryPath, fullBinaryDestination)
 
 	err = css.createSystemdServiceFile()
 	if err != nil {
 		return err
 	}
 
+	err = css.stopIfSystemdServiceIsActive()
+	if err != nil {
+		return err
+	}
+
 	err = css.enableSystemdService()
 	if err != nil {
 		return err
diff --git a/lib/config/hadeployconfig.go b/lib/config/hadeployconfig.go
index 272c57276..b451f0490 100644
--- a/lib/config/hadeployconfig.go
+++ b/lib/config/hadeployconfig.go
@@ -214,6 +214,10 @@ type AwsExternalOsSettings struct {
 	OsSnapshotUserAccessKeySecret string `toml:"os_snapshot_user_access_key_secret,omitempty"`
 }
 
+func NewHaDeployConfig() *HaDeployConfig {
+	return &HaDeployConfig{}
+}
+
 func (c *HaDeployConfig) Parse(configFile string) error {
 	fileUtils := &fileutils.FileSystemUtils{}
 	templateBytes, err := fileUtils.ReadFile(configFile)
diff --git a/lib/httputils/http.go b/lib/httputils/http.go
new file mode 100644
index 000000000..b43d0cc2d
--- /dev/null
+++ b/lib/httputils/http.go
@@ -0,0 +1,90 @@
+package httputils
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"time"
+
+	"github.com/chef/automate/lib/logger"
+)
+
+// HTTPClient interface represents an HTTP client.
+type HTTPClient interface {
+	MakeRequest(requestMethod, url string, body interface{}) (*http.Response, []byte, error)
+}
+
+// Client is a wrapper around http.Client that adds some convenience methods for making requests and handling responses.
+type Client struct {
+	Client *http.Client
+	Logger logger.Logger
+}
+
+// MockHTTPClient is a mock implementation of HTTPClient.
+type MockHTTPClient struct {
+	MakeRequestFunc func(requestMethod, url string, body interface{}) (*http.Response, []byte, error)
+}
+
+func (m *MockHTTPClient) MakeRequest(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
+	return m.MakeRequestFunc(requestMethod, url, body)
+}
+
+// NewClient returns a new Client with sane defaults.
+func NewClient(logger logger.Logger) *Client {
+	return &Client{
+		Client: http.DefaultClient,
+		Logger: logger,
+	}
+}
+
+// NewClientWithTimeout returns a new Client with the specified timeout.
+func NewClientWithTimeout(timeout time.Duration, logger logger.Logger) *Client {
+	return &Client{
+		Client: &http.Client{
+			Timeout: timeout,
+		},
+		Logger: logger,
+	}
+}
+
+// MakeRequest sends an HTTP request with the specified request method, URL, and body.
+func (c *Client) MakeRequest(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
+	var reader io.Reader
+
+	// Marshal the request body to JSON if it's not nil
+	if body != nil {
+		requestBody, err := json.Marshal(body)
+		if err != nil {
+			return nil, nil, fmt.Errorf("failed to marshal request body: %w", err)
+		}
+		c.Logger.Debugf("Request body for MakeRequest: %s\n", string(requestBody))
+		reader = bytes.NewReader(requestBody)
+	}
+
+	req, err := http.NewRequest(requestMethod, url, reader)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to create HTTP request: %w", err)
+	}
+
+	req.Header.Set("Content-Type", "application/json")
+
+	resp, err := c.Client.Do(req)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to make HTTP request: %w", err)
+	}
+
+	defer resp.Body.Close()
+
+	responseBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+		return resp, responseBody, fmt.Errorf("unexpected response status: %s", resp.Status)
+	}
+
+	return resp, responseBody, nil
+}
diff --git a/lib/sshutils/mocksshutils.go b/lib/sshutils/mocksshutils.go
index 9a1a6d2e5..d82882aec 100644
--- a/lib/sshutils/mocksshutils.go
+++ b/lib/sshutils/mocksshutils.go
@@ -1,10 +1,24 @@
 package sshutils
 
 type MockSSHUtilsImpl struct {
-	Executefunc      func(sshConfig SSHConfig, cmd string) (string, error)
+	Executefunc                      func(sshConfig SSHConfig, cmd string) (string, error)
+	ExecuteConcurrentlyFunc          func(sshConfig SSHConfig, cmd string, hostIPs []string) []Result
+	CopyFileToRemoteFunc             func(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool) error
+	CopyFileToRemoteConcurrentlyFunc func(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []Result
 }
 
 func (mssh *MockSSHUtilsImpl) Execute(sshConfig SSHConfig, cmd string) (string, error) {
 	return mssh.Executefunc(sshConfig, cmd)
 }
 
+func (mssh *MockSSHUtilsImpl) ExecuteConcurrently(sshConfig SSHConfig, cmd string, hostIPs []string) []Result {
+	return mssh.ExecuteConcurrentlyFunc(sshConfig, cmd, hostIPs)
+}
+
+func (mssh *MockSSHUtilsImpl) CopyFileToRemote(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool) error {
+	return mssh.CopyFileToRemoteFunc(sshConfig, srcFilePath, destFileName, removeFile)
+}
+
+func (mssh *MockSSHUtilsImpl) CopyFileToRemoteConcurrently(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []Result {
+	return mssh.CopyFileToRemoteConcurrentlyFunc(sshConfig, srcFilePath, destFileName, removeFile, hostIPs)
+}
diff --git a/lib/sshutils/sshutils.go b/lib/sshutils/sshutils.go
index 6ef9bd541..d975ed336 100644
--- a/lib/sshutils/sshutils.go
+++ b/lib/sshutils/sshutils.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"github.com/chef/automate/lib/logger"
+	"github.com/chef/automate/lib/platform/command"
 	"github.com/pkg/errors"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh/knownhosts"
@@ -25,10 +26,20 @@ type SSHConfig struct {
 type SSHUtilImpl struct {
 	SshClient ISshClient
 	logger    logger.Logger
+	Exec      command.Executor
 }
 
 type SSHUtil interface {
 	Execute(sshConfig SSHConfig, cmd string) (string, error)
+	ExecuteConcurrently(sshConfig SSHConfig, cmd string, hostIPs []string) []Result
+	CopyFileToRemote(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool) error
+	CopyFileToRemoteConcurrently(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []Result
+}
+
+type Result struct {
+	HostIP string
+	Output string
+	Error  error
 }
 
 func NewSSHUtil(sshclient ISshClient, logger logger.Logger) *SSHUtilImpl {
@@ -38,6 +49,14 @@ func NewSSHUtil(sshclient ISshClient, logger logger.Logger) *SSHUtilImpl {
 	}
 }
 
+func NewSSHUtilWithCommandExecutor(sshclient ISshClient, logger logger.Logger, exec command.Executor) *SSHUtilImpl {
+	return &SSHUtilImpl{
+		SshClient: sshclient,
+		logger:    logger,
+		Exec:      exec,
+	}
+}
+
 func NewSshConfigWithTimeout(ip string, port string, keyFile string, userName string, timeout int) SSHConfig {
 	return SSHConfig{
 		SshUser:    userName,
@@ -171,6 +190,112 @@ func (s *SSHUtilImpl) Execute(sshConfig SSHConfig, cmd string) (string, error) {
 	return output, nil
 }
 
+func (s *SSHUtilImpl) ExecuteConcurrently(sshConfig SSHConfig, cmd string, hostIPs []string) []Result {
+	resultChan := make(chan Result, len(hostIPs))
+
+	for _, hostIP := range hostIPs {
+		sshConfig.HostIP = hostIP
+		s.logger.Debugln("Connecting to the node : " + hostIP)
+
+		go func(sshConfig SSHConfig, cmd string, resultChan chan Result) {
+			rc := Result{sshConfig.HostIP, "", nil}
+
+			output, err := s.Execute(sshConfig, cmd)
+			if err != nil {
+				rc.Error = err
+				rc.Output = output
+				resultChan <- rc
+				return
+			}
+
+			if strings.Contains(strings.ToUpper(strings.TrimSpace(output)), "ERROR") {
+				rc.Error = errors.New(output)
+				rc.Output = output
+				resultChan <- rc
+				return
+			}
+
+			rc.Output = output
+			resultChan <- rc
+		}(sshConfig, cmd, resultChan)
+	}
+
+	var results []Result
+
+	for i := 0; i < len(hostIPs); i++ {
+		result := <-resultChan
+
+		if result.Error != nil {
+			s.logger.Errorf("Remote execution failed on node : %s with error: %v\n", result.HostIP, result.Error)
+		} else {
+			s.logger.Debugf("Remote execution is completed. Output for Host IP %s : \n%s\n", result.HostIP, result.Output)
+		}
+
+		results = append(results, result)
+	}
+	close(resultChan)
+	return results
+}
+
+func (s *SSHUtilImpl) CopyFileToRemote(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool) error {
+	cmd := "scp"
+	args := []string{"-P " + sshConfig.SshPort, "-o StrictHostKeyChecking=no", "-i", sshConfig.SshKeyFile, "-r", srcFilePath, sshConfig.SshUser + "@" + sshConfig.HostIP + ":/tmp/" + destFileName}
+	if err := s.Exec.Run(cmd, command.Args(args...)); err != nil {
+		s.logger.Errorf("Failed to copy file %s to remote with error: %v\n", srcFilePath, err)
+		if srcFilePath == "/usr/bin/chef-automate" {
+			s.logger.Errorln("Please copy your chef-automate binary to /usr/bin")
+		}
+		return err
+	}
+	if removeFile {
+		cmd := "rm"
+		args := []string{"-rf", srcFilePath}
+		if err := s.Exec.Run(cmd, command.Args(args...)); err != nil {
+			s.logger.Errorf("Failed to remove source file with error: %v\n", err)
+			return err
+		}
+	}
+	return nil
+}
+
+func (s *SSHUtilImpl) CopyFileToRemoteConcurrently(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []Result {
+	resultChan := make(chan Result, len(hostIPs))
+
+	for _, hostIP := range hostIPs {
+		sshConfig.HostIP = hostIP
+		s.logger.Debugln("Copying to the node : " + hostIP)
+
+		go func(sshConfig SSHConfig, srcFilePath string, destFileName string, removeFile bool, resultChan chan Result) {
+			rc := Result{sshConfig.HostIP, "", nil}
+
+			err := s.CopyFileToRemote(sshConfig, srcFilePath, destFileName, removeFile)
+			if err != nil {
+				rc.Error = err
+				resultChan <- rc
+				return
+			}
+
+			resultChan <- rc
+		}(sshConfig, srcFilePath, destFileName, removeFile, resultChan)
+	}
+
+	var results []Result
+
+	for i := 0; i < len(hostIPs); i++ {
+		result := <-resultChan
+
+		if result.Error != nil {
+			s.logger.Error("Remote copying failed on node : " + result.HostIP + " with error:\n" + result.Error.Error() + "\n")
+		} else {
+			s.logger.Debugf("Remote copying of file %s is completed on node at %s:/tmp/%s\n", srcFilePath, result.HostIP, destFileName)
+		}
+
+		results = append(results, result)
+	}
+	close(resultChan)
+	return results
+}
+
 func (s *SSHUtilImpl) CreateKnownHosts() error {
 	f, fErr := os.OpenFile(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"), os.O_CREATE, 0600)
 	if fErr != nil {
diff --git a/lib/stringutils/slice.go b/lib/stringutils/slice.go
index 476bda488..7c4f6ac07 100644
--- a/lib/stringutils/slice.go
+++ b/lib/stringutils/slice.go
@@ -96,6 +96,11 @@ func SliceReject(haystack []string, needle string) []string {
 	return res
 }
 
+func ConcatSlice(first, second []string) []string {
+	n := len(first)
+	return append(first[:n:n], second...)
+}
+
 func GetFullPlatformName(name, release string) string {
 	return fmt.Sprintf("%s %s", name, release)
 }


Test patch:
diff --git a/components/automate-cli/cmd/chef-automate/verify_test.go b/components/automate-cli/cmd/chef-automate/verify_test.go
index 6cc2ee3f5..355273560 100644
--- a/components/automate-cli/cmd/chef-automate/verify_test.go
+++ b/components/automate-cli/cmd/chef-automate/verify_test.go
@@ -1,908 +1,160 @@
 package main
 
 import (
+	"errors"
+	"net/http"
 	"testing"
 
-	verification "github.com/chef/automate/lib/verification"
-	"github.com/pkg/errors"
-	"github.com/spf13/cobra"
+	"github.com/chef/automate/components/automate-cli/pkg/verifysystemdcreate"
+	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/httputils"
+	"github.com/chef/automate/lib/majorupgrade_utils"
+	"github.com/chef/automate/lib/sshutils"
 	"github.com/stretchr/testify/assert"
 )
 
 const (
-	configFileName       = "config.toml"
-	parseConfigFileError = "Unable to parse config file"
+	CONFIG_FILE = "/config_valid_config_parser.toml"
 )
 
-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
+		description              string
+		mockHttputils            *httputils.MockHTTPClient
+		mockCreateSystemdService *verifysystemdcreate.MockCreateSystemdService
+		mockSystemdCreateUtils   *verifysystemdcreate.MockSystemdCreateUtils
+		mockSSHUtil              *sshutils.MockSSHUtilsImpl
+		configFile               string
+		wantErr                  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,
+			description: "bastion with existing automate-verify - success",
+			mockHttputils: &httputils.MockHTTPClient{
+				MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
+					return &http.Response{
+						StatusCode: http.StatusOK,
+						Body:       nil,
+					}, []byte(`{"status":"success"}`), nil
 				},
 			},
-			A2HARBFileExist: true,
-			mockFunc: &verification.VerificationMock{
-				VerifyHAAWSProvisionFunc: func(configFile string) error {
+			mockCreateSystemdService: &verifysystemdcreate.MockCreateSystemdService{
+				CreateFun: func() 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,
+			mockSystemdCreateUtils: &verifysystemdcreate.MockSystemdCreateUtils{
+				GetBinaryPathFunc: func() (string, error) {
+					return "", nil
 				},
 			},
-			A2HARBFileExist: false,
-			mockFunc: &verification.VerificationMock{
-				VerifyHAAWSManagedProvisionFunc: func(configFile string) error {
-					return nil
+			mockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
 				},
-			},
-			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,
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
 				},
 			},
-			A2HARBFileExist: false,
-			mockFunc: &verification.VerificationMock{
-				VerifyOnPremDeploymentFunc: func(configFile string) error {
-					return nil
-				},
-			},
-			ManagedServicesOn: false,
-			wantErr:           false,
+			configFile: CONFIG_TOML_PATH + CONFIG_FILE,
+			wantErr:    nil,
 		},
 		{
-			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,
+			description: "bastion without automate-verify - success",
+			mockHttputils: &httputils.MockHTTPClient{
+				MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
+					if requestMethod == http.MethodGet {
+						return nil, nil, errors.New("some error occurred")
+					}
+					return &http.Response{
+						StatusCode: http.StatusOK,
+						Body:       nil,
+					}, []byte(`{"status":"success"}`), nil
 				},
 			},
-			A2HARBFileExist: false,
-			mockFunc: &verification.VerificationMock{
-				VerifyOnPremAWSManagedDeploymentFunc: func(configFile string) error {
+			mockCreateSystemdService: &verifysystemdcreate.MockCreateSystemdService{
+				CreateFun: func() 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,
+			mockSystemdCreateUtils: &verifysystemdcreate.MockSystemdCreateUtils{
+				GetBinaryPathFunc: func() (string, error) {
+					return "", nil
 				},
 			},
-			A2HARBFileExist: false,
-			mockFunc: &verification.VerificationMock{
-				VerifyOnPremCustManagedDeploymentFunc: func(configFile string) error {
-					return nil
+			mockSSHUtil: &sshutils.MockSSHUtilsImpl{
+				ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
 				},
-			},
-			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,
+				CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, removeFile bool, hostIPs []string) []sshutils.Result {
+					return []sshutils.Result{
+						{
+							HostIP: "",
+							Error:  nil,
+							Output: "",
+						},
+					}
 				},
 			},
-			A2HARBFileExist: true,
-			mockFunc: &verification.VerificationMock{
-				VerifyHAAWSDeploymentFunc: func(configFile string) error {
-					return nil
-				},
-			},
-			ManagedServicesOn: false,
-			wantErr:           false,
+			configFile: CONFIG_TOML_PATH + CONFIG_FILE,
+			wantErr:    nil,
 		},
-		{
-			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)
+		t.Run(tt.description, func(t *testing.T) {
+			cw := majorupgrade_utils.NewCustomWriter()
 
-			if tt.wantErr {
-				assert.EqualError(t, err, tt.err.Error())
-			} else {
-				assert.Equal(t, nil, err)
-			}
-		})
-	}
-}
+			vc := NewVerifyCmdFlow(tt.mockHttputils,
+				tt.mockCreateSystemdService,
+				tt.mockSystemdCreateUtils,
+				config.NewHaDeployConfig(),
+				tt.mockSSHUtil,
+				cw.CliWriter)
 
-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,
+			flagsObj := &verifyCmdFlags{
+				config: tt.configFile,
 			}
-			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())
+			err := vc.runVerifyCmd(nil, nil, flagsObj)
+			if tt.wantErr != nil {
+				assert.Error(t, err)
+				assert.ErrorContains(t, err, tt.wantErr.Error())
 			} else {
-				assert.Equal(t, nil, err)
+				assert.NoError(t, 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 TestVerifyCmdFunc(t *testing.T) {
+	flagsObj := &verifyCmdFlags{
+		config: CONFIG_TOML_PATH + CONFIG_FILE,
+		debug:  true,
 	}
-}
 
-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)
-			}
-		})
-	}
-}
+	vf := verifyCmdFunc(flagsObj)
+	assert.NotNil(t, vf)
 
-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)
-			}
-		})
-	}
+	err := vf(nil, nil)
+	assert.Error(t, err)
+	assert.ErrorContains(t, err, "Cannot create automate-verify service since systemd is not present on this machine")
 }
diff --git a/components/automate-cli/pkg/testfiles/onprem/config_valid_config_parser.toml b/components/automate-cli/pkg/testfiles/onprem/config_valid_config_parser.toml
new file mode 100644
index 000000000..f56495caf
--- /dev/null
+++ b/components/automate-cli/pkg/testfiles/onprem/config_valid_config_parser.toml
@@ -0,0 +1,213 @@
+
+# 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 instances with default settings.
+
+[architecture.existing_infra]
+## === INPUT NEEDED ===
+# Eg.: ssh_user = "ubuntu"
+ssh_user = "ec2-user"
+
+# custom ssh group name, it will be defaulted to ssh_user
+# Eg.: ssh_group_name = "ubuntu"
+ssh_group_name = ""
+
+# private ssh key file path to access instances
+# Eg.: ssh_key_file = "~/.ssh/A2HA.pem"
+ssh_key_file = "../../../../lib/config/testdata/A2HA.pem"
+
+# custome ssh port no to connect instances, default will be 22
+# Eg.: ssh_port = "22"
+ssh_port = "22"
+
+secrets_key_file = "/hab/a2_deploy_workspace/secrets.key"
+secrets_store_file = "/hab/a2_deploy_workspace/secrets.json"
+architecture = "existing_nodes"
+workspace_path = "/hab/a2_deploy_workspace"
+# DON'T MODIFY THE BELOW LINE (backup_mount)
+backup_mount = "/mnt/automate_backups"
+
+# Eg.: backup_config = "object_storage" or "file_system"
+backup_config = "file_system"
+
+# If backup_config = "object_storage" fill out [object_storage.config] as well 
+## Object storage similar to AWS S3 Bucket
+[object_storage.config]
+bucket_name = ""
+access_key = ""
+secret_key = ""
+# For S3 bucket, default endpoint value is "https://s3.amazonaws.com"
+# Include protocol to the enpoint value. Eg: https://customdns1.com or http://customdns2.com
+endpoint = ""
+
+# [Optional] Mention object_storage region if applicable
+# Eg: region = "us-west-1"
+region = ""
+## === ===
+
+# ============== EC2 Nodes Config ======================
+[automate.config]
+## === INPUT NEEDED ===
+
+# Password for Automate UI for 'admin' user.
+admin_password = "chefautomate"
+
+
+# Automate Load Balancer FQDN eg.: "chefautomate.example.com"
+fqdn = "A2-cba94fe5-automate-lb-1993603048.ap-northeast-2.elb.amazonaws.com"
+
+# No. of Automate Frontend Machine or VM eg.: instance_count = "2"
+instance_count = "2"
+
+## === ===
+
+
+# teams_port = ""
+config_file = "configs/automate.toml"
+
+# Set enable_custom_certs = true to provide custom certificates during deployment
+enable_custom_certs = false
+
+# Add Automate Load Balancer root-ca
+# root_ca = """root_ca_contents"""
+
+# Add Automate node internal public and private keys
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+
+# [[automate.config.certs_by_ip]]
+# ip = ""
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+
+[chef_server.config]
+## === INPUT NEEDED ===
+
+# No. of Chef Server Frontend Machine or VM eg.: instance_count = "2"
+instance_count = "2"
+
+# Set enable_custom_certs = true to provide custom certificates during deployment
+enable_custom_certs = false
+
+# Add Chef Server node internal public and private keys
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+
+# [[chef_server.config.certs_by_ip]]
+# ip = ""
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+
+## === ===
+
+[opensearch.config]
+## === INPUT NEEDED ===
+
+# No. of OpenSearch DB Backend Machine or VM eg.: instance_count = "3"
+instance_count = "3"
+
+# Set enable_custom_certs = true to provide custom certificates during deployment
+enable_custom_certs = false
+# Add OpenSearch root-ca and keys
+# root_ca = """root_ca_contents"""
+# admin_key = """admin_private_key_contents"""
+# admin_cert = """admin_public_key_contents"""
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+# [[opensearch.config.certs_by_ip]]
+# ip = ""
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+
+## === ===
+
+[postgresql.config]
+## === INPUT NEEDED ===
+
+# No. of Postgresql DB Backend Machine or VM eg.: instance_count = "3"
+instance_count = "3"
+
+# Set enable_custom_certs = true to provide custom certificates during deployment
+enable_custom_certs = false
+# Add postgresql root-ca and keys
+# root_ca = """root_ca_contents"""
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+# [[postgresql.config.certs_by_ip]]
+# ip = ""
+# private_key = """private_key_contents"""
+# public_key = """public_key_contents"""
+
+## === ===
+
+[existing_infra.config]
+## === INPUT NEEDED ===
+
+# provide comma-seperated IP addresses of nodes, like ["192.0.0.1", "192.0.0.2", "192.0.0.2"]
+# No. of IP address should be the same as the No. of instance_count count mentioned above in
+# automate.config, chef_server.config, opensearch.config and postgresql.config
+automate_private_ips = ["10.1.0.36","10.1.1.113"]
+chef_server_private_ips = ["10.1.0.41","10.1.1.108"]
+opensearch_private_ips = ["10.1.0.116","10.1.1.32","10.1.2.250"]
+postgresql_private_ips = ["10.1.0.23","10.1.1.217","10.1.2.223"]
+## === ===
+
+# ============== External Datbase Services ======================
+
+## === INPUT NEEDED ===
+# In case you are trying to deploy with AWS Managed Services set type as "aws"
+# If you are trying external managed database set type as "self-managed"
+
+[external.database]
+
+# eg type = "aws" or "self-managed"
+type = ""
+
+[external.database.postgre_sql]
+
+# eg: instance_url = "managed-rds-db.c5gkx.ap-northeast-1.rds.amazonaws.com:5432"
+instance_url = ""
+
+# eg: username = "postgres"
+superuser_username = ""
+
+# eg: password = "Progress123"
+superuser_password = ""
+
+# eg: dbuser_username = "postgres"
+dbuser_username = ""
+
+# eg: dbuser_password = "Progress123"
+dbuser_password = ""
+
+# In case of AWS managed RDS leave it blank
+postgresql_root_cert = "<cert_content>"
+
+[external.database.open_search]
+
+# eg: managed_opensearch_domain_name = "managed-services-os"
+opensearch_domain_name = ""
+
+# eg: opensearch_domain_url = "search-managed-services-os.us-east-1.es.amazonaws.com"
+opensearch_domain_url = ""
+
+# eg: opensearch_username = "admin"
+opensearch_username = ""
+
+# eg: opensearch_user_password = "Progress@123"
+opensearch_user_password = ""
+
+# In case of AWS managed opensearch leave it blank
+opensearch_root_cert = "<cert_content>"
+
+[external.database.open_search.aws]
+
+# eg: aws_os_snapshot_role_arn = "arn:aws:iam::1127583934333:role/managed-services"
+aws_os_snapshot_role_arn = ""
+
+# eg: os_snapshot_user_access_key_id = "AKIA..........PQS7Q7A"
+os_snapshot_user_access_key_id = ""
+
+# eg: os_snapshot_user_access_key_secret = "skP4Mqihj....................anAXAX"
+os_snapshot_user_access_key_secret = ""
+
diff --git a/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go b/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go
index 40bc1acb5..bba10ff4e 100644
--- a/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go
+++ b/components/automate-cli/pkg/verifyserver/server/api/v1/status_test.go
@@ -73,13 +73,13 @@ func TestStatusAPI(t *testing.T) {
 			description:  "200:success status route",
 			expectedCode: 200,
 			isError:      false,
-			expectedBody: "{\"status\":\"SUCCESS\",\"result\":{\"status\":\"OK\",\"services\":[{\"service_name\":\"deployment-service\",\"status\":\"OK\",\"version\":\"chef/deployment-service/0.1.0/20230502070345\"}],\"error\":\"\"}}",
+			expectedBody: "{\"status\":\"SUCCESS\",\"result\":{\"status\":\"OK\",\"services\":[{\"service_name\":\"deployment-service\",\"status\":\"OK\",\"version\":\"chef/deployment-service/0.1.0/20230502070345\"}],\"cli_version\":\"unknown\",\"error\":\"\"}}",
 		},
 		{
 			description:  "200:with error status route",
 			expectedCode: 200,
 			isError:      true,
-			expectedBody: "{\"status\":\"SUCCESS\",\"result\":{\"status\":\"OK\",\"services\":[],\"error\":\"some error occurred\"}}",
+			expectedBody: "{\"status\":\"SUCCESS\",\"result\":{\"status\":\"OK\",\"services\":[],\"cli_version\":\"unknown\",\"error\":\"some error occurred\"}}",
 		},
 	}
 	statusEndpoint := "/status"
diff --git a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc_test.go b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc_test.go
index 1caf059f4..08358e07d 100644
--- a/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc_test.go
+++ b/components/automate-cli/pkg/verifysystemdcreate/systemdcreatesvc_test.go
@@ -3,7 +3,6 @@ package verifysystemdcreate_test
 import (
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"testing"
 
@@ -104,19 +103,18 @@ func TestSystemdServiceCreate(t *testing.T) {
 		assert.NoError(t, err)
 		_, err = os.Stat(systemdLocation + "/automate-verify.service")
 		assert.NoError(t, err)
-		assert.Contains(t, cw.Output(), "Binary copied to "+binaryDestinationFolder)
 		assert.Contains(t, cw.Output(), "Service automate-verify created successfully")
 	})
 
-	t.Run("it gives error if there automate-verify systemd-service is already enabled", func(t *testing.T) {
+	t.Run("it restart if there automate-verify systemd-service is already enabled", func(t *testing.T) {
 		mockUtils := setupMockUtils()
 		mockUtils.ExecuteShellCommandFunc = executeShellCommandPassIsEnabled
 		binaryDestinationFolder, systemdLocation := setupCopy(t)
 		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
 		assert.NoError(t, err)
 		err = vsc.Create()
-		assert.Error(t, err)
-		assert.Equal(t, "Service automate-verify already exists on this node, use systemctl start/stop/status automate-verify", err.Error())
+		assert.NoError(t, err)
+		assert.Contains(t, cw.Output(), "Service automate-verify created successfully")
 	})
 
 	t.Run("it gives error if there is problem in running shell command", func(t *testing.T) {
@@ -169,17 +167,16 @@ func TestSystemdServiceCreate(t *testing.T) {
 		binaryDestinationFolder, systemdLocation := setupCopy(t)
 		systemdFilePath := systemdLocation + "/automate-verify.service"
 		srcData := []byte("test data")
-		assert.NoError(t, ioutil.WriteFile(systemdFilePath, srcData, 0700))
+		assert.NoError(t, os.WriteFile(systemdFilePath, srcData, 0700))
 		vsc, err := verifysystemdcreate.NewCreateSystemdService(mockUtils, binaryDestinationFolder, systemdLocation, false, cw.CliWriter)
 		assert.NoError(t, err)
 		err = vsc.Create()
 		assert.NoError(t, err)
 		_, err = os.Stat(systemdFilePath)
 		assert.NoError(t, err)
-		dstData, err := ioutil.ReadFile(systemdFilePath)
+		dstData, err := os.ReadFile(systemdFilePath)
 		assert.NoError(t, err)
 		assert.Contains(t, string(dstData), "Description=Service for automating verification")
-		assert.Contains(t, cw.Output(), "Binary copied to "+binaryDestinationFolder+"\nService automate-verify created successfully\n")
 	})
 
 	t.Run("it creates automate-verify.service file with proper content inside", func(t *testing.T) {
@@ -205,10 +202,9 @@ WantedBy=multi-user.target
 		assert.NoError(t, err)
 		_, err = os.Stat(systemdFilePath)
 		assert.NoError(t, err)
-		dstData, err := ioutil.ReadFile(systemdFilePath)
+		dstData, err := os.ReadFile(systemdFilePath)
 		assert.NoError(t, err)
 		assert.Equal(t, fmt.Sprintf(expectedSystemdContents, binaryDestinationFolder), string(dstData))
-		assert.Contains(t, cw.Output(), "Binary copied to "+binaryDestinationFolder+"\nService automate-verify created successfully\n")
 	})
 
 }
diff --git a/lib/httputils/http_test.go b/lib/httputils/http_test.go
new file mode 100644
index 000000000..566312a8a
--- /dev/null
+++ b/lib/httputils/http_test.go
@@ -0,0 +1,118 @@
+package httputils_test
+
+import (
+	"encoding/json"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+	"time"
+
+	"github.com/chef/automate/lib/httputils"
+	"github.com/chef/automate/lib/logger"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMakeRequestSuccessfulRequest(t *testing.T) {
+	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		assert.Equal(t, http.MethodPost, r.Method)
+		assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
+
+		body, err := io.ReadAll(r.Body)
+		assert.NoError(t, err)
+
+		var requestBody map[string]interface{}
+		err = json.Unmarshal(body, &requestBody)
+		assert.NoError(t, err)
+
+		expectedBody := map[string]interface{}{
+			"key1": "value1",
+			"key2": "42",
+		}
+		assert.Equal(t, expectedBody, requestBody)
+
+		w.WriteHeader(http.StatusOK)
+		w.Write([]byte("Success"))
+	}))
+	defer server.Close()
+
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	client := httputils.NewClient(logger)
+
+	url := server.URL
+	requestBody := map[string]interface{}{
+		"key1": "value1",
+		"key2": "42",
+	}
+	resp, responseBody, err := client.MakeRequest(http.MethodPost, url, requestBody)
+
+	assert.NoError(t, err)
+	assert.NotNil(t, resp)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+
+	expectedResponseBody := "Success"
+	assert.Equal(t, expectedResponseBody, string(responseBody))
+}
+
+func TestMakeRequestErrorResponse(t *testing.T) {
+	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(http.StatusInternalServerError)
+		w.Write([]byte("Internal Server Error"))
+	}))
+	defer server.Close()
+
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	client := httputils.NewClient(logger)
+
+	url := server.URL
+	resp, responseBody, err := client.MakeRequest(http.MethodGet, url, nil)
+
+	assert.Error(t, err)
+	assert.NotNil(t, resp)
+	assert.Contains(t, err.Error(), "unexpected response status")
+	assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
+	assert.Equal(t, "Internal Server Error", string(responseBody))
+}
+
+func TestMakeRequestRequestBodyError(t *testing.T) {
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	client := httputils.NewClient(logger)
+
+	resp, responseBody, err := client.MakeRequest(http.MethodPost, "https://example.com", make(chan int))
+
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+	assert.Nil(t, responseBody)
+	assert.Contains(t, err.Error(), "failed to marshal request body")
+}
+
+func TestMakeRequestRequestError(t *testing.T) {
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+	client := httputils.NewClient(logger)
+
+	resp, responseBody, err := client.MakeRequest(http.MethodGet, "invalid-url", nil)
+
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+	assert.Nil(t, responseBody)
+	assert.Contains(t, err.Error(), "failed to make HTTP request")
+}
+
+func TestMakeRequestConnectionError(t *testing.T) {
+	logger, err := logger.NewLogger("text", "debug")
+	assert.NoError(t, err)
+
+	client := httputils.NewClientWithTimeout(1*time.Millisecond, logger)
+
+	resp, responseBody, err := client.MakeRequest(http.MethodGet, "http://non-existent-server", nil)
+
+	assert.Error(t, err)
+	assert.Nil(t, resp)
+	assert.Nil(t, responseBody)
+	assert.Contains(t, err.Error(), "context deadline exceeded")
+}
diff --git a/lib/sshutils/sshutils_test.go b/lib/sshutils/sshutils_test.go
index 5dda35797..ccb52cfca 100644
--- a/lib/sshutils/sshutils_test.go
+++ b/lib/sshutils/sshutils_test.go
@@ -9,26 +9,35 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/chef/automate/lib/logger"
+	"github.com/chef/automate/lib/platform/command"
 	"github.com/chef/automate/lib/sshutils"
 	"golang.org/x/crypto/ssh"
 )
 
 const (
-	testfile        = `./testfiles/ssh`
-	sudoPassword    = "123456"
-	sudoPasswordCmd = `echo 123456 | sudo -S ls -l`
-	dialFailed      = `dial failed:ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain`
-	nodeIp          = "1.1.1.1"
-	userName        = "ubuntu"
-	port            = "22"
-	timeout         = 150
+	testfile                   = `./testfiles/ssh`
+	sudoPassword               = "123456"
+	sudoPasswordCmd            = `echo 123456 | sudo -S ls -l`
+	dialFailed                 = `dial failed:ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain`
+	nodeIp1                    = "1.1.1.1"
+	nodeIp2                    = "1.1.1.2"
+	nodeIp3                    = "1.1.1.3"
+	userName                   = "ubuntu"
+	port                       = "22"
+	timeout                    = 150
+	totalZero                  = "total 0"
+	sessionCreationFailedError = "Session creation failed"
+	srcFilePath                = "/some/file/path"
+	destFileName               = "abc.txt"
+	remoteCopyError            = "error while copying file to remote"
+	outputWithError            = "some error output"
 )
 
 var sshConfig = sshutils.SSHConfig{
 	SshUser:    "ubuntu",
 	SshPort:    "22",
 	SshKeyFile: testfile,
-	HostIP:     "1.1.1.1",
+	HostIP:     nodeIp1,
 	Timeout:    150,
 }
 
@@ -215,14 +224,14 @@ func TestExecute(t *testing.T) {
 						return nil, nil
 					},
 					CombinedOutputfunc: func(cmd string, session *ssh.Session) ([]byte, error) {
-						return []byte("total 0"), nil
+						return []byte(totalZero), nil
 					},
 					Closefunc: func(s *ssh.Session) error {
 						return nil
 					},
 				},
 			},
-			want:    "total 0",
+			want:    totalZero,
 			wantErr: nil,
 		},
 		{
@@ -270,7 +279,7 @@ func TestExecute(t *testing.T) {
 						return nil
 					},
 					NewSessionfunc: func(c *ssh.Client) (*ssh.Session, error) {
-						return nil, errors.New("Session creation failed")
+						return nil, errors.New(sessionCreationFailedError)
 					},
 					CombinedOutputfunc: func(cmd string, session *ssh.Session) ([]byte, error) {
 						return nil, nil
@@ -280,8 +289,8 @@ func TestExecute(t *testing.T) {
 					},
 				},
 			},
-			want:    "Session creation failed",
-			wantErr: errors.New("Session creation failed"),
+			want:    sessionCreationFailedError,
+			wantErr: errors.New(sessionCreationFailedError),
 		},
 		{
 			description: "Combined Output not able to produce the desired output",
@@ -354,6 +363,256 @@ func TestExecute(t *testing.T) {
 	}
 }
 
+func TestExecuteConcurrently(t *testing.T) {
+	log, _ := logger.NewLogger("text", "debug")
+	sshUtil := sshutils.NewSSHUtil(&sshutils.SshClient{}, log)
+
+	tests := []struct {
+		description   string
+		sshConfig     sshutils.SSHConfig
+		cmd           string
+		hostIPs       []string
+		MockSshClient sshutils.ISshClient
+		want          string
+		wantErr       error
+	}{
+		{
+			description: "Execute command concurrently - success",
+			sshConfig:   sshConfig,
+			cmd:         "sudo whoami",
+			hostIPs:     []string{nodeIp1, nodeIp2, nodeIp3},
+			MockSshClient: &sshutils.MockSshClient{
+				Dialfunc: func(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
+					return &ssh.Client{}, nil
+				},
+				ParsePrivateKeyfunc: func(pemBytes []byte) (ssh.Signer, error) {
+					return nil, nil
+				},
+				PublicKeyfunc: func(signers ssh.Signer) ssh.AuthMethod {
+					return nil
+				},
+				NewSessionfunc: func(c *ssh.Client) (*ssh.Session, error) {
+					return nil, nil
+				},
+				CombinedOutputfunc: func(cmd string, session *ssh.Session) ([]byte, error) {
+					return []byte("root"), nil
+				},
+				Closefunc: func(s *ssh.Session) error {
+					return nil
+				},
+			},
+			want:    "root",
+			wantErr: nil,
+		},
+		{
+			description: "Execute command concurrently - failure",
+			sshConfig:   sshConfig,
+			cmd:         "some command",
+			hostIPs:     []string{nodeIp1, nodeIp2, nodeIp3},
+			MockSshClient: &sshutils.MockSshClient{
+				Dialfunc: func(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
+					return &ssh.Client{}, nil
+				},
+				ParsePrivateKeyfunc: func(pemBytes []byte) (ssh.Signer, error) {
+					return nil, nil
+				},
+				PublicKeyfunc: func(signers ssh.Signer) ssh.AuthMethod {
+					return nil
+				},
+				NewSessionfunc: func(c *ssh.Client) (*ssh.Session, error) {
+					return nil, nil
+				},
+				CombinedOutputfunc: func(cmd string, session *ssh.Session) ([]byte, error) {
+					return []byte(""), errors.New("error while executing command on the remote host:Process exited with status 1")
+				},
+				Closefunc: func(s *ssh.Session) error {
+					return nil
+				},
+			},
+			want:    "",
+			wantErr: errors.New("error while executing command on the remote host:Process exited with status 1"),
+		},
+		{
+			description: "Execute command concurrently - error in output",
+			sshConfig:   sshConfig,
+			cmd:         "sudo whoami",
+			hostIPs:     []string{nodeIp1, nodeIp2, nodeIp3},
+			MockSshClient: &sshutils.MockSshClient{
+				Dialfunc: func(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
+					return &ssh.Client{}, nil
+				},
+				ParsePrivateKeyfunc: func(pemBytes []byte) (ssh.Signer, error) {
+					return nil, nil
+				},
+				PublicKeyfunc: func(signers ssh.Signer) ssh.AuthMethod {
+					return nil
+				},
+				NewSessionfunc: func(c *ssh.Client) (*ssh.Session, error) {
+					return nil, nil
+				},
+				CombinedOutputfunc: func(cmd string, session *ssh.Session) ([]byte, error) {
+					return []byte(outputWithError), nil
+				},
+				Closefunc: func(s *ssh.Session) error {
+					return nil
+				},
+			},
+			want:    outputWithError,
+			wantErr: errors.New(outputWithError),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			sshUtil.SshClient = tt.MockSshClient
+			results := sshUtil.ExecuteConcurrently(tt.sshConfig, tt.cmd, tt.hostIPs)
+			for _, result := range results {
+				if tt.wantErr != nil {
+					assert.ErrorContains(t, result.Error, tt.wantErr.Error())
+				}
+				assert.Equal(t, result.Output, tt.want)
+			}
+		})
+	}
+
+}
+
+func TestCopyFileToRemote(t *testing.T) {
+	log, _ := logger.NewLogger("text", "debug")
+	sshUtil := sshutils.NewSSHUtil(&sshutils.SshClient{}, log)
+
+	tests := []struct {
+		description  string
+		sshConfig    sshutils.SSHConfig
+		exec         command.Executor
+		srcFilePath  string
+		destFileName string
+		removeFile   bool
+		wantErr      error
+	}{
+		{
+			description: "copy file to remote - success",
+			sshConfig:   sshConfig,
+			exec: &command.MockExecutorImpl{
+				RunFunc: func(cmd string, opts ...command.Opt) error {
+					return nil
+				},
+			},
+			srcFilePath:  srcFilePath,
+			destFileName: destFileName,
+			removeFile:   false,
+			wantErr:      nil,
+		},
+		{
+			description: "copy file to remote - failure",
+			sshConfig:   sshConfig,
+			exec: &command.MockExecutorImpl{
+				RunFunc: func(cmd string, opts ...command.Opt) error {
+					return errors.New(remoteCopyError)
+				},
+			},
+			srcFilePath:  "/usr/bin/chef-automate",
+			destFileName: destFileName,
+			removeFile:   false,
+			wantErr:      errors.New(remoteCopyError),
+		},
+		{
+			description: "copy file to remote and delete source file - success",
+			sshConfig:   sshConfig,
+			exec: &command.MockExecutorImpl{
+				RunFunc: func(cmd string, opts ...command.Opt) error {
+					return nil
+				},
+			},
+			srcFilePath:  srcFilePath,
+			destFileName: destFileName,
+			removeFile:   true,
+			wantErr:      nil,
+		},
+		{
+			description: "copy file to remote and delete source file - failure",
+			sshConfig:   sshConfig,
+			exec: &command.MockExecutorImpl{
+				RunFunc: func(cmd string, opts ...command.Opt) error {
+					if cmd == "rm" {
+						return errors.New(remoteCopyError)
+					}
+					return nil
+				},
+			},
+			srcFilePath:  srcFilePath,
+			destFileName: destFileName,
+			removeFile:   true,
+			wantErr:      errors.New(remoteCopyError),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			sshUtil.Exec = tt.exec
+			err := sshUtil.CopyFileToRemote(tt.sshConfig, tt.srcFilePath, tt.destFileName, tt.removeFile)
+			assert.Equal(t, err, tt.wantErr)
+		})
+	}
+}
+
+func TestCopyFileToRemoteConcurrently(t *testing.T) {
+	log, _ := logger.NewLogger("text", "debug")
+	sshUtil := sshutils.NewSSHUtilWithCommandExecutor(&sshutils.SshClient{}, log, command.NewMockExecutor(t))
+
+	tests := []struct {
+		description  string
+		sshConfig    sshutils.SSHConfig
+		exec         command.Executor
+		srcFilePath  string
+		destFileName string
+		removeFile   bool
+		hostIPs      []string
+		wantErr      error
+	}{
+		{
+			description: "copy file to remote concurrently - success",
+			sshConfig:   sshConfig,
+			exec: &command.MockExecutorImpl{
+				RunFunc: func(cmd string, opts ...command.Opt) error {
+					return nil
+				},
+			},
+			srcFilePath:  srcFilePath,
+			destFileName: destFileName,
+			removeFile:   false,
+			hostIPs:      []string{nodeIp1, nodeIp2, nodeIp3},
+			wantErr:      nil,
+		},
+		{
+			description: "copy file to remote concurrently - failure",
+			sshConfig:   sshConfig,
+			exec: &command.MockExecutorImpl{
+				RunFunc: func(cmd string, opts ...command.Opt) error {
+					return errors.New(remoteCopyError)
+				},
+			},
+			srcFilePath:  srcFilePath,
+			destFileName: destFileName,
+			removeFile:   false,
+			hostIPs:      []string{nodeIp1, nodeIp2, nodeIp3},
+			wantErr:      errors.New(remoteCopyError),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.description, func(t *testing.T) {
+			sshUtil.Exec = tt.exec
+			results := sshUtil.CopyFileToRemoteConcurrently(tt.sshConfig, tt.srcFilePath, tt.destFileName, tt.removeFile, tt.hostIPs)
+			for _, result := range results {
+				if tt.wantErr != nil {
+					assert.ErrorContains(t, result.Error, tt.wantErr.Error())
+				}
+			}
+		})
+	}
+}
+
 func TestCheckKnownHosts(t *testing.T) {
 	log, _ := logger.NewLogger("text", "debug")
 	sshu := sshutils.NewSSHUtil(&sshutils.SshClient{}, log)
@@ -436,9 +695,9 @@ func TestAddHostKey(t *testing.T) {
 		{
 			description: "If the Adding host key was successfully done",
 			args: args{
-				host: nodeIp,
+				host: nodeIp1,
 				remote: &netAddressTest{
-					Address: nodeIp,
+					Address: nodeIp1,
 				},
 				pubkey: &sshPublicKeyTest{
 					key:  "",
@@ -484,9 +743,9 @@ func TestHostCallKeyBack(t *testing.T) {
 		{
 			description: "Client Config creation is a success",
 			args: args{
-				host: nodeIp,
+				host: nodeIp1,
 				remote: &netAddressTest{
-					Address: nodeIp,
+					Address: nodeIp1,
 				},
 				pubkey: &sshPublicKeyTest{
 					key:  "",
@@ -523,7 +782,7 @@ func TestNewSshConfigWithTimeout(t *testing.T) {
 		timeout  int
 	}
 	var arg args = args{
-		ip:       nodeIp,
+		ip:       nodeIp1,
 		port:     port,
 		keyfile:  testfile,
 		userName: userName,
@@ -533,7 +792,7 @@ func TestNewSshConfigWithTimeout(t *testing.T) {
 		SshUser:    "ubuntu",
 		SshPort:    "22",
 		SshKeyFile: testfile,
-		HostIP:     "1.1.1.1",
+		HostIP:     nodeIp1,
 		Timeout:    150,
 	}
 	response := sshutils.NewSshConfigWithTimeout(arg.ip, arg.port, arg.keyfile, arg.userName, arg.timeout)
@@ -548,7 +807,7 @@ func TestNewSshConfig(t *testing.T) {
 		userName string
 	}
 	var arg args = args{
-		ip:       nodeIp,
+		ip:       nodeIp1,
 		port:     port,
 		keyfile:  testfile,
 		userName: userName,
@@ -557,8 +816,8 @@ func TestNewSshConfig(t *testing.T) {
 		SshUser:    "ubuntu",
 		SshPort:    "22",
 		SshKeyFile: testfile,
-		HostIP:     "1.1.1.1",
+		HostIP:     nodeIp1,
 	}
 	response := sshutils.NewSshConfig(arg.ip, arg.port, arg.keyfile, arg.userName)
 	assert.Equal(t, response, want)
-}
\ No newline at end of file
+}
diff --git a/lib/stringutils/slice_test.go b/lib/stringutils/slice_test.go
index 335cabc6a..8242e93f7 100644
--- a/lib/stringutils/slice_test.go
+++ b/lib/stringutils/slice_test.go
@@ -109,4 +109,12 @@ func TestSliceIntersection(t *testing.T) {
 	b := []string{"hardware-check", "ssh-user"}
 	diff := stringutils.SliceIntersection(a, b)
 	assert.Equal(t, []string{"hardware-check"}, diff)
-}
\ No newline at end of file
+}
+
+func TestConcatSlice(t *testing.T) {
+	a := []string{"1", "2", "4"}
+	b := []string{"3", "5", "6"}
+	concat := stringutils.ConcatSlice(a, b)
+	assert.Equal(t, []string{"1", "2", "4", "3", "5", "6"}, concat)
+
+}