Repository: chef/automate
Pull request #7923 (2023-05-27 05:42:36)

config gen initial work
### :nut_and_bolt: Description: What code changed, and why?

Initial work on the Config gen is added here.
This will help generate Automate configs very easily with less mistakes.

This PR will contain Automate HA on Premise deployment config generation.

<!-- /!\ Please ensure that you are NOT disclosing any customer information without their consent /!\ -->

### :chains: Related Resources

https://chefio.atlassian.net/browse/CHEF-3313

FINAL DEMO VIDEO:
https://progresssoftware.sharepoint.com/:v:/s/ChefCoreC/EZJxepsuv25NtwXVpffEWRABib0VYzb14gc3ILaxpd2XPA?e=nugEyf

### :+1: Definition of Done

### :athletic_shoe: How to Build and Test the Change

To build:
1. `cd components/automate-cli`
2. `make darwin` or `make linux`
3. `sudo ./bin/darwin/chef-automate config gen config.toml` or `sudo ./bin/linux/chef-automate config gen config.toml`

### :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
FINAL DEMO VIDEO: https://progresssoftware.sharepoint.com/:v:/s/ChefCoreC/EZJxepsuv25NtwXVpffEWRABib0VYzb14gc3ILaxpd2XPA?e=nugEyf

![Screenshot 2023-05-27 at 11 11 35 AM](https://github.com/chef/automate/assets/4770182/9df08167-a346-4dbc-8b4c-da7fb3013855)
![Screenshot 2023-05-27 at 11 12 12 AM](https://github.com/chef/automate/assets/4770182/7befb376-04e5-4363-9d86-9e9f77c586b8)







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


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | a8f380e838ce4d14e8a3c3523600678dcc9d75a5 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/chef-automate/deploys/6471984f0fa7710008ad9c63 |
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=7923)

[![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=7923) [![0.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 0.9% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![5.4%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '5.4%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 5.4% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![4.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '4.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 4.6% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![4.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '4.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 4.6% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![4.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '4.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 4.6% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![5.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '5.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 5.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![5.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '5.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 5.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![2.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '2.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 2.6% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![2.2%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '2.2%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 2.2% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![2.2%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '2.2%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 2.2% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![0.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.0%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 0.0% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![0.0%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.0%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 0.0% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![2.2%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '2.2%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 2.2% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![0.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 0.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![0.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 0.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![0.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 0.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![0.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '0.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 0.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![12.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '12.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 12.9% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![12.9%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '12.9%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 12.9% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![11.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '11.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 11.8% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![11.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '11.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 11.8% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![27.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '27.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 27.8% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![27.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '27.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 27.8% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![7.2%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/0-16px.png '7.2%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 7.2% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.65% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![25.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '25.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 25.6% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.58% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![25.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '25.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 25.6% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.58% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![25.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '25.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 25.6% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.58% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![25.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '25.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 25.6% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.58% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![33.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '33.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 33.3% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.58% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![48.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/40-16px.png '48.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 48.8% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.58% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![48.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/40-16px.png '48.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 48.8% Coverage on New Code (is less than 80%)  
[![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=7923) [![3.6%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/Duplications/5-16px.png '3.6%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 3.58% Duplicated Lines (%) on New Code (is greater than 3%)  

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


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=7923)

[![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=7923) [![51.7%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/50-16px.png '51.7%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 51.7% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![53.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/50-16px.png '53.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 53.1% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![68.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '68.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 68.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![68.3%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '68.3%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 68.3% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![77.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '77.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 77.1% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![77.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '77.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 77.1% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![77.1%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '77.1%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 77.1% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923) [![79.8%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/60-16px.png '79.8%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 79.8% Coverage on New Code (is less than 80%)  

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


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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.5% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.5% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.5% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.5% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.4% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.4% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.4% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923&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=7923&resolved=false&types=BUG) [0 Bugs](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&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=7923&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923) [![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=7923) [0 Security Hotspots](https://sonar.progress.com/security_hotspots?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923)  
[![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=7923&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=7923&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonar.progress.com/project/issues?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_coverage&view=list) [83.4% Coverage](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonar.progress.com/component_measures?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923&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=7923)

[![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=7923) [![25.7%](https://raw.githubusercontent.com/SonarSource/sonarqube-static-resources/master/v97/checks/CoverageChart/25-16px.png '25.7%')](https://sonar.progress.com/dashboard?id=chef_automate_AYeOVEbqJ4YHsO5MtKWC&pullRequest=7923) 25.7% Coverage on New Code (is less than 80%)  

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



Patch:
diff --git a/.bldr.toml b/.bldr.toml
index 71ef8a143..3af3b47d2 100644
--- a/.bldr.toml
+++ b/.bldr.toml
@@ -447,6 +447,7 @@ paths = [
   "lib/db/*",
   "lib/executil/*",
   "lib/grpc/*",
+  "lib/hardwarecal/*",
   "lib/httputils/*",
   "lib/io/*",
   "lib/logger/*",
@@ -455,6 +456,7 @@ paths = [
   "lib/platform/config/*",
   "lib/platform/pg/*",
   "lib/platform/sys/*",
+  "lib/pmt/*",
   "lib/proc/*",
   "lib/product/*",
   "lib/proxy/*",
@@ -464,6 +466,7 @@ paths = [
   "lib/stringutils/*",
   "lib/systemresource/*",
   "lib/tls/*",
+  "lib/toml/*",
   "lib/tracing/*",
   "lib/user/*",
   "lib/userutils/*",
diff --git a/VERSION b/VERSION
index 807a48905..135b3fd8d 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.7.202
\ No newline at end of file
+4.7.204
\ No newline at end of file
diff --git a/components/automate-cli/cmd/chef-automate/config.go b/components/automate-cli/cmd/chef-automate/config.go
index 9a6db64e1..555a54ec3 100644
--- a/components/automate-cli/cmd/chef-automate/config.go
+++ b/components/automate-cli/cmd/chef-automate/config.go
@@ -1,7 +1,6 @@
 package main
 
 import (
-	"io/ioutil"
 	"reflect"
 	"regexp"
 	"strings"
@@ -19,6 +18,9 @@ import (
 	"github.com/chef/automate/components/automate-cli/pkg/status"
 	"github.com/chef/automate/components/automate-deployment/pkg/cli"
 	"github.com/chef/automate/components/automate-deployment/pkg/client"
+	"github.com/chef/automate/lib/config/genconfig"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/pmt"
 	"github.com/chef/automate/lib/stringutils"
 	"github.com/chef/toml"
 	"github.com/imdario/mergo"
@@ -60,6 +62,10 @@ func init() {
 	configCmd.AddCommand(showConfigCmd)
 	configCmd.AddCommand(patchConfigCmd)
 	configCmd.AddCommand(setConfigCmd)
+	configCmd.AddCommand(genConfigCmd)
+
+	//config gen flags
+	genConfigCmd.Flags().BoolVarP(&configCmdFlags.overwriteFile, "overwrite", "O", false, "Overwrite existing config.toml")
 
 	//config show flags
 	showConfigCmd.Flags().BoolVarP(&configCmdFlags.overwriteFile, "overwrite", "O", false, "Overwrite existing config.toml [Standalone]")
@@ -148,6 +154,17 @@ var showConfigCmd = &cobra.Command{
 	},
 }
 
+var genConfigCmd = &cobra.Command{
+	Use:   "gen [/path/to/write/config.toml]",
+	Short: "generate the Automate HA configuration",
+	Long:  "Prompt based Config Generation command. It will output the config in the provided file, if file path is not provided then it will print on STDOUT.",
+	RunE:  runGenCmd,
+	Args:  cobra.RangeArgs(0, 2),
+	Annotations: map[string]string{
+		docs.Tag: docs.BastionHost,
+	},
+}
+
 var patchConfigCmd = &cobra.Command{
 	Use:   "patch path/to/config.toml",
 	Short: "patch the Chef Automate configuration", Long: "Apply a partial Chef Automate configuration to the deployment. It will take the partial configuration, merge it with the existing configuration, and apply and required changes.",
@@ -169,6 +186,70 @@ var setConfigCmd = &cobra.Command{
 	},
 }
 
+func runGenCmd(cmd *cobra.Command, args []string) error {
+	fsu := fileutils.NewFileSystemUtils()
+	p := pmt.PromptFactory(os.Stdin, os.Stdout, fsu)
+	g := genconfig.GenConfigImpFactory(p)
+
+	err := g.GenConfigWithPrompts()
+	if err != nil {
+		return status.Wrap(
+			err,
+			status.FailedToGenConfig,
+			"Failed to generate config with given input values to the prompts.",
+		)
+	}
+	t, err := g.Toml()
+	if err != nil {
+		return status.Wrap(
+			err,
+			status.MarshalError,
+			"Marshaling configuration to TOML failed",
+		)
+	}
+
+	// Handle writing to a file if a path was given
+	if len(args) > 0 && args[0] != "" {
+		outFile, err := filepath.Abs(args[0])
+		if err != nil {
+			return status.Annotate(err, status.FileAccessError)
+		}
+
+		err = checkConfigGenFileExist(outFile, fsu)
+		if err != nil {
+			return err
+		}
+		err = fsu.WriteFile(outFile, t, 0644)
+		if err != nil {
+			return status.Wrap(err, status.FileAccessError, fmt.Sprint("Failed to write to file: ", outFile))
+		}
+	} else {
+		writer.Println("")
+		writer.Println(string(t))
+	}
+
+	status.GlobalResult = t
+	return nil
+}
+
+func checkConfigGenFileExist(outFile string, fsu fileutils.FileUtils) error {
+	if _, err := fsu.Stat(outFile); err == nil {
+		if configCmdFlags.overwriteFile {
+			return nil
+		}
+		p := pmt.PromptFactory(os.Stdin, os.Stdout, fsu)
+		ok, err := p.Confirm(fmt.Sprintf("%s file already exists. Do you wish to overwrite it?", outFile), "yes", "no")
+		if err != nil {
+			return status.Wrap(err, status.PromptFailed, err.Error())
+		}
+		if !ok {
+			err = errors.New("failed to confirm overwrite")
+			return status.Annotate(err, status.FileAccessError)
+		}
+	}
+	return nil
+}
+
 func runShowCmd(cmd *cobra.Command, args []string) error {
 
 	if isA2HARBFileExist() {
@@ -821,7 +902,7 @@ func getMergedOpensearchInterface(rawOutput string, pemFilePath string, remoteSe
 		return "", err
 	}
 
-	pemBytes, err := ioutil.ReadFile(pemFilePath) // nosemgrep
+	pemBytes, err := os.ReadFile(pemFilePath)
 	if err != nil {
 		return "", err
 	}
@@ -844,7 +925,7 @@ func getMergedPostgresqlInterface(rawOutput string, pemFilePath string, remoteSe
 		return "", err
 	}
 
-	pemBytes, err := ioutil.ReadFile(pemFilePath) // nosemgrep
+	pemBytes, err := os.ReadFile(pemFilePath)
 	if err != nil {
 		return "", err
 	}
@@ -961,7 +1042,7 @@ func getDecodedConfig(input string, remoteService string) (interface{}, error) {
 
 // getConfigForArgsPostgresqlAndOpenSearch gets the requested config from the args provided for postgresql or opensearch
 func getConfigForArgsPostgresqlOrOpenSearch(args []string, remoteService string) (interface{}, error) {
-	pemBytes, err := ioutil.ReadFile(args[0]) // nosemgrep
+	pemBytes, err := os.ReadFile(args[0])
 	if err != nil {
 		return nil, err
 	}
@@ -1070,7 +1151,7 @@ func createTomlFileFromConfig(config interface{}, tomlFile string) (string, erro
 
 func parseAndRemoveRestrictedKeysFromSrcFile(srcString string) (string, error) {
 
-	tomlbyt, _ := ioutil.ReadFile(srcString) // nosemgrep
+	tomlbyt, _ := os.ReadFile(srcString)
 	destString := string(tomlbyt)
 	var dest dc.AutomateConfig
 	if _, err := toml.Decode(destString, &dest); err != nil {
diff --git a/components/automate-cli/cmd/chef-automate/hardwarecal.go b/components/automate-cli/cmd/chef-automate/hardwarecal.go
new file mode 100644
index 000000000..286cd23b9
--- /dev/null
+++ b/components/automate-cli/cmd/chef-automate/hardwarecal.go
@@ -0,0 +1,60 @@
+package main
+
+import (
+	"os"
+
+	"github.com/chef/automate/components/automate-cli/pkg/docs"
+	"github.com/chef/automate/components/automate-cli/pkg/status"
+	"github.com/chef/automate/lib/hardwarecal"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/pmt"
+	"github.com/spf13/cobra"
+)
+
+func init() {
+	RootCmd.AddCommand(hardwareCalCmd())
+}
+
+func hardwareCalCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "hardware-cal COMMAND",
+		Short: "Estimate Hardware Requirements for given inputs, this calculator is for Chef Automate HA",
+		Long:  "Estimate Hardware Requirements for given inputs, this calculator is for Chef Automate HA",
+		Annotations: map[string]string{
+			docs.Tag: docs.BastionHost,
+		},
+		Hidden: true,
+	}
+
+	run := &cobra.Command{
+		Use:   "run",
+		Short: "This will trigger the hardware calculator to ask input values, based on which it will provide hardware requirements for Chef Automate HA",
+		Long:  "This will trigger the hardware calculator to ask input values, based on which it will provide hardware requirements for Chef Automate HA",
+		RunE:  runCal,
+		Annotations: map[string]string{
+			docs.Tag: docs.BastionHost,
+		},
+		Hidden: true,
+	}
+
+	cmd.AddCommand(run)
+	return cmd
+}
+
+func runCal(*cobra.Command, []string) error {
+	cw := writer
+	fsu := &fileutils.FileSystemUtils{}
+	p := pmt.PromptFactory(os.Stdin, os.Stdout, fsu)
+	hc := hardwarecal.NewHardwareCalPrompt(p, cw)
+	err := hc.Run()
+	if err != nil {
+		return status.Wrap(
+			err,
+			status.HardwareCalError,
+			"Failed to Estimate Hardware Requirements",
+		)
+	}
+
+	cw.Println("* These numbers are just for estimation, based on our test environment conditions. \n* Please do your performance analysis based on your environment and usages.")
+	return nil
+}
diff --git a/components/automate-cli/cmd/chef-automate/sshUtils.go b/components/automate-cli/cmd/chef-automate/sshUtils.go
index a13ae10cb..07f182b26 100644
--- a/components/automate-cli/cmd/chef-automate/sshUtils.go
+++ b/components/automate-cli/cmd/chef-automate/sshUtils.go
@@ -11,15 +11,21 @@ import (
 	"path/filepath"
 	"regexp"
 	"strings"
+	"sync"
 	"time"
 	"unicode"
 
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/sshutils"
+	"github.com/chef/automate/lib/stringutils"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh/knownhosts"
 )
 
+var mutex sync.Mutex
+
 type SSHConfig struct {
 	sshUser    string
 	sshPort    string
@@ -83,12 +89,36 @@ func (s *SSHUtilImpl) getClientConfig() (*ssh.ClientConfig, error) {
 	var (
 		keyErr *knownhosts.KeyError
 	)
+	homePath := os.Getenv("HOME")
+	if homePath == "" {
+		errStr := "Environment variable HOME cannot be empty. Please set a value for HOME env"
+		writer.Errorln(errStr)
+		writer.BufferWriter().Flush()
+		return nil, errors.New(errStr)
+	}
+	sshDirPath := filepath.Join(os.Getenv("HOME"), ".ssh")
+	exists, err := fileutils.PathExists(sshDirPath)
+	if err != nil {
+		errStr := fmt.Sprintf("Error evaluating path: %v\n", err)
+		writer.Errorln(errStr)
+		writer.BufferWriter().Flush()
+		return nil, errors.New(errStr)
+	}
+	if !exists {
+		errStr := fmt.Sprintf("Path %s does not exist", sshDirPath)
+		writer.Errorln(errStr)
+		writer.BufferWriter().Flush()
+		return nil, errors.New(errStr)
+	}
 	// Client config
 	return &ssh.ClientConfig{
 		User: s.SshConfig.sshUser,
 		Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
 		HostKeyCallback: ssh.HostKeyCallback(func(host string, remote net.Addr, pubKey ssh.PublicKey) error {
-			kh := checkKnownHosts()
+			mutex.Lock()
+			defer mutex.Unlock()
+			knownHostPath := filepath.Join(sshDirPath, sshutils.AUTOMATE_KNOWN_HOSTS)
+			kh := checkKnownHosts(knownHostPath)
 			hErr := kh(host, remote, pubKey)
 			// Reference: https://blog.golang.org/go1.13-errors
 			// To understand what errors.As is.
@@ -101,7 +131,7 @@ func (s *SSHUtilImpl) getClientConfig() (*ssh.ClientConfig, error) {
 			} else if errors.As(hErr, &keyErr) && len(keyErr.Want) == 0 {
 				// host key not found in known_hosts then give a warning and continue to connect.
 				// writer.Printf("WARNING: %s is not trusted, adding this key to known_hosts file.\n", host)
-				return addHostKey(host, remote, pubKey)
+				return addHostKey(host, knownHostPath, remote, pubKey)
 			}
 			// writer.Printf("Pub key exists for %s.\n", host)
 			return nil
@@ -129,8 +159,8 @@ func (s *SSHUtilImpl) getConnection() (*ssh.Client, error) {
 	return conn, err
 }
 
-func createKnownHosts() {
-	f, fErr := os.OpenFile(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"), os.O_CREATE, 0600)
+func createKnownHosts(knownHostPath string) {
+	f, fErr := os.OpenFile(knownHostPath, os.O_CREATE, 0600)
 	if fErr != nil {
 		writer.Errorf("%v", fErr)
 		return
@@ -138,9 +168,9 @@ func createKnownHosts() {
 	f.Close()
 }
 
-func checkKnownHosts() ssh.HostKeyCallback {
-	createKnownHosts()
-	kh, e := knownhosts.New(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
+func checkKnownHosts(knownHostPath string) ssh.HostKeyCallback {
+	createKnownHosts(knownHostPath)
+	kh, e := knownhosts.New(knownHostPath)
 	if e != nil {
 		writer.Errorf("%v", e)
 		return nil
@@ -148,10 +178,17 @@ func checkKnownHosts() ssh.HostKeyCallback {
 	return kh
 }
 
-func addHostKey(host string, remote net.Addr, pubKey ssh.PublicKey) error {
+func addHostKey(host, khFilePath string, remote net.Addr, pubKey ssh.PublicKey) error {
+
+	content, err := os.ReadFile(khFilePath)
+	if err != nil {
+		writer.Errorf("Error while reading the known hosts file:%v", err)
+		return err
+	}
+	lastLine := stringutils.GetLastLine(string(content))
+
 	// add host key if host is not found in known_hosts, error object is return, if nil then connection proceeds,
 	// if not nil then connection stops.
-	khFilePath := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
 
 	f, fErr := os.OpenFile(khFilePath, os.O_APPEND|os.O_WRONLY, 0600)
 	if fErr != nil {
@@ -160,9 +197,12 @@ func addHostKey(host string, remote net.Addr, pubKey ssh.PublicKey) error {
 	defer f.Close()
 
 	knownHosts := knownhosts.Normalize(remote.String())
-	_, fileErr := f.WriteString(knownhosts.Line([]string{knownHosts}, pubKey))
-	f.WriteString("\n")
-	return fileErr
+	line := knownhosts.Line([]string{knownHosts}, pubKey)
+	if lastLine != "" {
+		line = "\n" + line
+	}
+	_, err = f.WriteString(line)
+	return err
 }
 
 func (s *SSHUtilImpl) connectAndExecuteCommandOnRemote(remoteCommands string, spinner bool) (string, error) {
diff --git a/components/automate-cli/cmd/chef-automate/tfvarsToJson.go b/components/automate-cli/cmd/chef-automate/tfvarsToJson.go
index d25e81191..f184df6af 100644
--- a/components/automate-cli/cmd/chef-automate/tfvarsToJson.go
+++ b/components/automate-cli/cmd/chef-automate/tfvarsToJson.go
@@ -12,18 +12,18 @@ import (
 
 func convTfvarToJson(filename string) string {
 	if len(strings.TrimSpace(filename)) < 1 {
-		writer.Printf("Invalid or empty argument")
+		writer.Println("Invalid or empty argument")
 		return ""
 	}
 	src, err := ioutil.ReadFile(filename) // nosemgrep
 	if err != nil {
-		writer.Printf("Error reading %q: %s", filename, err)
+		writer.Printf("Error reading %q: %s\n", filename, err)
 		return ""
 	}
 
 	f, diags := hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1})
 	if diags.HasErrors() {
-		writer.Printf("Error parsing %q: %s", filename, diags.Error())
+		writer.Printf("Error parsing %q: %s\n", filename, diags.Error())
 		return ""
 	}
 	if f.Body == nil {
@@ -33,7 +33,7 @@ func convTfvarToJson(filename string) string {
 
 	attrs, diags := f.Body.JustAttributes()
 	if diags.HasErrors() {
-		writer.Printf("Error evaluating %q: %s", filename, diags.Error())
+		writer.Printf("Error evaluating %q: %s\n", filename, diags.Error())
 		return ""
 	}
 
@@ -41,12 +41,12 @@ func convTfvarToJson(filename string) string {
 	for name, attr := range attrs {
 		value, diags := attr.Expr.Value(nil)
 		if diags.HasErrors() {
-			writer.Printf("Error evaluating %q in %q: %s", name, filename, diags.Error())
+			writer.Printf("Error evaluating %q in %q: %s\n", name, filename, diags.Error())
 			return ""
 		}
 		buf, err := ctyjson.Marshal(value, value.Type())
 		if err != nil {
-			writer.Printf("Error converting %q in %q to JSON: %s", name, filename, err)
+			writer.Printf("Error converting %q in %q to JSON: %s\n", name, filename, err)
 			return ""
 		}
 		values[name] = json.RawMessage(buf)
@@ -54,7 +54,7 @@ func convTfvarToJson(filename string) string {
 
 	output, err := json.MarshalIndent(values, "", "  ")
 	if err != nil {
-		writer.Printf("Error marshalling %q to JSON: %s", filename, err)
+		writer.Printf("Error marshalling %q to JSON: %s\n", filename, err)
 		return ""
 	}
 	return string(output)
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
index 41db17033..85e4f3c9f 100644
--- a/components/automate-cli/cmd/chef-automate/verify.go
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -316,7 +316,7 @@ func (v *verifyCmdFlow) RunVerify(config string) error {
 		v.sshPort, v.sshKeyFile, v.sshUserName = v.getSSHConfig(v.Config.Architecture.Aws)
 		configDetails, err := fetchAwsConfigFromTerraform()
 		if err != nil {
-			return status.New(status.ConfigVerifyError, err.Error())
+			return status.Wrap(errors.New("Cannot run verify command before provision in AWS deployment.\nPlease run 'chef-automate provision-infra config.toml --airgap-bundle automate.aib' to provision your infra"), status.ConfigVerifyError, err.Error())
 		}
 
 		v.automateIPs = configDetails.AutomateIps
diff --git a/components/automate-cli/pkg/status/error.go b/components/automate-cli/pkg/status/error.go
index 6b2dd44d7..bdcd35795 100644
--- a/components/automate-cli/pkg/status/error.go
+++ b/components/automate-cli/pkg/status/error.go
@@ -50,6 +50,9 @@ const (
 const (
 	VerifyChecksError                 = 125
 	ConfigVerifyError                 = 124
+	PromptFailed                      = 126
+	FailedToGenConfig                 = 127
+	HardwareCalError                  = 128
 	IpAccessError                     = 123
 	InsufficientSpaceError            = 122
 	CalDestDirSizeError               = 121
@@ -104,6 +107,9 @@ const (
 var ErrorMetadata = map[int][]string{
 	VerifyChecksError:                 {"125", "VerifyChecksError", "Verify Checks failed"},
 	ConfigVerifyError:                 {"124", "ConfigVerifyError", "Config Verification failed"},
+	HardwareCalError:                  {"128", "HardwareCalError", "Hardware Calculator failed to provide Hardware Estimates"},
+	PromptFailed:                      {"126", "PromptFailed", "Prompt failed and exited with error"},
+	FailedToGenConfig:                 {"127", "FailedToGenConfig", "Failed to Generate config with given inputs"},
 	InsufficientSpaceError:            {"122", "InsufficientSpaceError", "Insufficient disk space"},
 	CalDestDirSizeError:               {"121", "CalDestDirSizeError", "Error in calculating Dest directory size"},
 	CalESDirSizeError:                 {"120", "CalESDirSizeError", "Error in calculating ES directory size"},
diff --git a/components/docs-chef-io/content/automate/ha_aws_deployment_prerequisites.md b/components/docs-chef-io/content/automate/ha_aws_deployment_prerequisites.md
index 72616b5a1..1a3e31505 100644
--- a/components/docs-chef-io/content/automate/ha_aws_deployment_prerequisites.md
+++ b/components/docs-chef-io/content/automate/ha_aws_deployment_prerequisites.md
@@ -120,14 +120,14 @@ The Chef Automate HA cluster requires multiple ports for the frontend and backen
 
 The first column in the table below represents the source of the connection. The table's other columns represent the destination with the matrix value as a port number. The specified port numbers need to be opened on the origin and destination.
 
-|                 | Chef Automate        | Chef Infra Server    | Postgresql                                | OpenSearch                           | Bastion | Load Balancer |
+|                 | Chef Automate        | Chef Infra Server    | Postgresql                                | OpenSearch                           | Bastion | Automate Load Balancer |
 | --------------- | -------------------- | -------------------- | ----------------------------------------- | ------------------------------------ | ------- | ------------- |
 | Chef Automate   |                      |                      | 7432                                      | 9200                                 |         |               |
-| Infra Server    | 443                  |                      | 7432                                      | 9200                                 |         |               |
+| Infra Server    |                      |                      | 7432                                      | 9200                                 |         | 443              |
 | PostgreSQL      |                      |                      | 9631, 7432, 5432, 6432, 9638<br/>UDP 9638 |                                      |         |               |
 | OpenSearch      |                      |                      |                                           | 9631, 9200, 9300, 9638 <br/>UDP 9638 |         |               |
 | Bastion         | 22, 9631, 9638, 7799 | 22, 9631, 9638, 7799 | 22, 9631, 9638, 7432, 7799                | 22, 9631, 9638, 9200, 7799           |         | 22            |
-| Load Balancer   | 443, 80              | 443, 80              |                                           |                                      |         |               |
+| Automate Load Balancer   | 443, 80              | 443, 80              |                                           |                                      |         |               |
 | Internet Access |                      |                      |                                           |                                      | 80, 443 |               |
 
 {{< note >}} Custom SSH port is supported, but use the same port across all the machines. {{< /note >}}
diff --git a/components/docs-chef-io/content/automate/ha_on_premises_deployment_prerequisites.md b/components/docs-chef-io/content/automate/ha_on_premises_deployment_prerequisites.md
index 3bd02be15..34ede3e06 100644
--- a/components/docs-chef-io/content/automate/ha_on_premises_deployment_prerequisites.md
+++ b/components/docs-chef-io/content/automate/ha_on_premises_deployment_prerequisites.md
@@ -123,14 +123,14 @@ The Chef Automate HA cluster requires multiple ports for the frontend and backen
 
 The first column in the table below represents the source of the connection. The table's other columns represent the destination with the matrix value as a port number. The specified port numbers need to be opened on the origin and destination.
 
-|               | Chef Automate  | Chef Infra Server | Postgresql                          | OpenSearch                           | Bastion | Load Balancer |
+|               | Chef Automate  | Chef Infra Server | Postgresql                          | OpenSearch                           | Bastion | Automate Load Balancer |
 |---------------|----------------|-------------------|-------------------------------------|--------------------------------------|---------| ------------- |
 | Chef Automate |                |                   | 7432                                | 9200                                 |         |               |
-| Infra Server  | 443            |                   | 7432                                | 9200                                 |         |               |
+| Infra Server  |                |                   | 7432                                | 9200                                 |         | 443              |
 | PostgreSQL    |                |                   | 9631, 7432, 5432, 6432, 9638<br/>UDP 9638 |                                      |         |               |
 | OpenSearch    |                |                   |                                     | 9631, 9200, 9300, 9638 <br/>UDP 9638 |         |               |
 | Bastion       | 22, 9631, 9638, 7799 | 22, 9631, 9638, 7799    | 22, 9631, 9638, 7432, 7799                | 22, 9631, 9638, 9200, 7799                 |         | 22            |
-| Load Balancer | 443, 80        | 443, 80           |                                     |                                      |         |               |
+| Automate Load Balancer | 443, 80        | 443, 80           |                                     |                                      |         |               |
 
 {{< note >}} Custom SSH port is supported, but use the same port across all the machines. {{< /note >}}
 
diff --git a/go.mod b/go.mod
index c894b477d..695b6e972 100644
--- a/go.mod
+++ b/go.mod
@@ -116,6 +116,8 @@ require (
 	github.com/shirou/gopsutil v3.21.11+incompatible
 )
 
+require github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
+
 require (
 	cloud.google.com/go v0.65.0 // indirect
 	cloud.google.com/go/storage v1.10.0 // indirect
@@ -183,6 +185,7 @@ require (
 	github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
 	github.com/magiconair/properties v1.8.1 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/manifoldco/promptui v0.9.0
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.18 // indirect
 	github.com/mattn/go-runewidth v0.0.14 // indirect
diff --git a/go.sum b/go.sum
index 2b4bc785a..9bf335690 100644
--- a/go.sum
+++ b/go.sum
@@ -172,8 +172,11 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chef/toml v0.3.1-0.20200730001027-920c30b33b5d h1:fVRXxFz5+MWhty4oarqLwi6dSkPZFYYEs5OJGLzEX5A=
 github.com/chef/toml v0.3.1-0.20200730001027-920c30b33b5d/go.mod h1:WzupinluQEwU43AC1/UZItixzo5R849ewfPDB2ZMDDg=
+github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
@@ -560,6 +563,8 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN
 github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
+github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
 github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -1039,6 +1044,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
diff --git a/lib/config/genconfig/automatehaconfig.go b/lib/config/genconfig/automatehaconfig.go
new file mode 100644
index 000000000..9f6b062fe
--- /dev/null
+++ b/lib/config/genconfig/automatehaconfig.go
@@ -0,0 +1,21 @@
+package genconfig
+
+import (
+	"github.com/chef/automate/lib/toml"
+)
+
+type AutomateHaConfig struct {
+	Fqdn string `json:"fqdn,omitempty" toml:"fqdn,omitempty" mapstructure:"fqdn,omitempty"`
+}
+
+func AutomateHaConfigFactory() *AutomateHaConfig {
+	return &AutomateHaConfig{}
+}
+
+func (c *AutomateHaConfig) Toml() (tomlBytes []byte, err error) {
+	return toml.Marshal(c)
+}
+
+func (c *AutomateHaConfig) Prompts() (err error) {
+	return
+}
diff --git a/lib/config/genconfig/awshaprovisionconfig.go b/lib/config/genconfig/awshaprovisionconfig.go
new file mode 100644
index 000000000..3b5fca0f6
--- /dev/null
+++ b/lib/config/genconfig/awshaprovisionconfig.go
@@ -0,0 +1,1287 @@
+package genconfig
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/pmt"
+	"github.com/chef/automate/lib/toml"
+)
+
+const (
+	AWS_MACHINE_TYPE_REGEX        = "^((a1|c1|c3|c4|c5|c5a|c5ad|c5d|c5n|c6a|c6g|c6gd|c6gn|c6i|c6id|c7g|cc2|d2|d3|d3en|dl1|f1|g2|g3|g3s|g4ad|g4dn|g5|g5g|h1|i2|i3|i3en|i4i|im4gn|inf1|is4gen|m1|m2|m3|m4|m5|m5a|m5ad|m5d|m5dn|m5n|m5zn|m6a|m6g|m6gd|m6i|m6id|mac1|mac2|p2|p3|p3dn|p4d|r3|r4|r5|r5a|r5ad|r5b|r5d|r5dn|r5n|r6a|r6g|r6gd|r6i|r6id|t1|t2|t3|t3a|t4g|trn1|u-12tb1|u-3tb1|u-6tb1|u-9tb1|vt1|x1|x1e|x2gd|x2idn|x2iedn|x2iezn|z1d)\\.(10xlarge|112xlarge|12xlarge|16xlarge|18xlarge|24xlarge|2xlarge|32xlarge|3xlarge|48xlarge|4xlarge|56xlarge|6xlarge|8xlarge|9xlarge|large|medium|metal|micro|nano|small|xlarge))$"
+	AWS_VOL_TYPE_REGEX            = "^(gp2|gp3|io2|io1|st1|sc1)$"
+	IP_REGEX                      = "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$"
+	URL_OPTIONAL_PORT_REGEX       = "^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](:{1}[0-9]{1,5})?$"
+	FQDN_REGEX                    = "^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]$"
+	URL_REQUIRED_PORT_REGEX       = "^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]:{1}[0-9]{1,5}$"
+	LINUX_USER_REGEX              = "^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30})$"
+	DIR_PATH_REGEX                = "^\\/$|(\\/[a-zA-Z_0-9-]+)+$"
+	BUCKET_NAME_REGEX             = "^[a-zA-Z0-9_-]+$"
+	AWS_ACCESS_KEY_ID_REGEX       = "^[A-Z0-9]{20}$"
+	AWS_ACCESS_KEY_SECRET_REGEX   = "^[A-Za-z0-9/+=]{40}$"
+	ACCESS_KEY_ID_REGEX           = "^[A-Za-z0-9/+=]+$"
+	ACCESS_KEY_SECRET_REGEX       = "^[A-Za-z0-9/+=]+$"
+	ENDPOINT_URL                  = "^((http|https)://)[-a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)$"
+	AUTOMATE_ADMIN_PASSWORD_REGEX = "^.{8,35}$"
+	OPENSEARCH_NODETYPE           = "OpenSearch"
+	POSTGRESQL_NODETYPE           = "Postgresql"
+	AUTOMATE_NODETYPE             = "Automate"
+	CHEF_INFRA_SERVER_NODETYPE    = "Chef Infra Server"
+	PUB_CERT                      = "Public Cert"
+	PRI_KEY                       = "Private Key"
+	ADMIN_CERT                    = "Admin Cert"
+	ADMIN_KEY                     = "Admin Key"
+	ROOTCA                        = "Root CA"
+)
+
+type AwsHaProvisionConfig struct {
+	Prompt         pmt.Prompt `toml:"-"`
+	Config         *config.HaDeployConfig
+	HasCustomCerts bool                `toml:"-"`
+	FileUtils      fileutils.FileUtils `toml:"-"`
+}
+
+func AwsHaProvisionConfigFactory(p pmt.Prompt) *AwsHaProvisionConfig {
+	return &AwsHaProvisionConfig{
+		Prompt:    p,
+		Config:    &config.HaDeployConfig{},
+		FileUtils: fileutils.NewFileSystemUtils(),
+	}
+}
+
+func (c *AwsHaProvisionConfig) Toml() (tomlBytes []byte, err error) {
+	return toml.Marshal(c.Config)
+}
+
+func (c *AwsHaProvisionConfig) Prompts() (err error) {
+	err = c.PromptAwsArchitecture()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAwsConfig()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomate()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServer()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptDatabases()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptBackup()
+	if err != nil {
+		return
+	}
+
+	err = c.Config.Verify()
+	if err != nil {
+		fmt.Println("ERRORs in Validation:\n", err)
+		return nil
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAwsConfig() (err error) {
+	err = c.PromptProfile()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptRegion()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptVpcId()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptCidr()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptSshKeyPairName()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAmi()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptDeleteOnTermination()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptLoadBalancerAccessLogs()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptCidr() (err error) {
+	isCidrBlockAddr, err := c.HasCidrBlockAddr()
+	if err != nil {
+		return
+	}
+
+	if !isCidrBlockAddr {
+		err1 := c.PromptPrivateSubnet()
+		if err1 != nil {
+			return err1
+		}
+		err1 = c.PromptPublicSubnet()
+		if err1 != nil {
+			return err1
+		}
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptProfile() (err error) {
+	profile, err := c.Prompt.InputStringDefault("AWS Profile", "default")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().Profile = profile
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptRegion() (err error) {
+	awsRegions := AwsRegionsImpFactory(c.Prompt)
+	awsRegion, err := awsRegions.Choose("AWS Region")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().Region = awsRegion
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptVpcId() (err error) {
+	vpcId, err := c.Prompt.InputStringRequired("AWS VPC ID")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AwsVpcID = vpcId
+	return
+}
+
+func (c *AwsHaProvisionConfig) HasCidrBlockAddr() (isCiderBlock bool, err error) {
+	isCiderBlock, err = c.Prompt.Confirm("Do you want to use AWS CIDR Block", "yes", "no")
+	if err != nil {
+		return
+	}
+	if isCiderBlock {
+		err = c.PromptCidrBlockAddr()
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptCidrBlockAddr() (err error) {
+	ciderBlockAddr, err := c.Prompt.InputStringRegex("AWS CIDR Block Address", IP_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AwsCidrBlockAddr = ciderBlockAddr
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPrivateSubnet() (err error) {
+	privateSubnet1, err := c.Prompt.InputStringRequired("AWS Private Subnet 1:")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().PrivateCustomSubnets = []string{privateSubnet1}
+
+	privateSubnet2, err := c.Prompt.InputStringRequired("AWS Private Subnet 2:")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().PrivateCustomSubnets = append(c.Config.InitAws().InitConfigAwsSettings().PrivateCustomSubnets, privateSubnet2)
+
+	privateSubnet3, err := c.Prompt.InputStringRequired("AWS Private Subnet 3:")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().PrivateCustomSubnets = append(c.Config.InitAws().InitConfigAwsSettings().PrivateCustomSubnets, privateSubnet3)
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPublicSubnet() (err error) {
+	hasPublicSubnets, err := c.Prompt.Confirm("Do you have Public Subnets", "yes", "no")
+	if err != nil {
+		return
+	}
+	if hasPublicSubnets {
+		publicSubnet1, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 1:")
+		if err1 != nil {
+			return err1
+		}
+		c.Config.InitAws().InitConfigAwsSettings().PublicCustomSubnets = []string{publicSubnet1}
+
+		publicSubnet2, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 2:")
+		if err1 != nil {
+			return err1
+		}
+		c.Config.InitAws().InitConfigAwsSettings().PublicCustomSubnets = append(c.Config.InitAws().InitConfigAwsSettings().PublicCustomSubnets, publicSubnet2)
+
+		publicSubnet3, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 3:")
+		if err1 != nil {
+			return err1
+		}
+		c.Config.InitAws().InitConfigAwsSettings().PublicCustomSubnets = append(c.Config.InitAws().InitConfigAwsSettings().PublicCustomSubnets, publicSubnet3)
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptSshKeyPairName() (err error) {
+	sshKeyPairName, err := c.Prompt.InputStringRequired("AWS SSH Key Pair Name")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().SSHKeyPairName = sshKeyPairName
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAmi() (err error) {
+	amiId, err := c.Prompt.InputStringRequired("AWS AMI ID")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AmiID = amiId
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptDeleteOnTermination() (err error) {
+	noDelOnTerm, err := c.Prompt.Confirm("Delete on termination should be", "off", "on")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().DeleteOnTermination = !noDelOnTerm
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptDatabases() (err error) {
+	isAwsManagedDb, err := c.PromptIsAwsManaged()
+	if err != nil {
+		return
+	}
+
+	if isAwsManagedDb {
+		err := c.PromptAwsManaged()
+		if err != nil {
+			return err
+		}
+		c.SetZeroDBNodes()
+	} else {
+		err := c.PromptChefManaged()
+		if err != nil {
+			return err
+		}
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptIsAwsManaged() (isAwsManagedDb bool, err error) {
+	isAwsManagedDb, err = c.Prompt.Confirm("Do you want to use AWS Managed Databases", "yes", "no")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().SetupManagedServices = isAwsManagedDb
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAwsManaged() (err error) {
+	err = c.PromptAwsManagedOpenSearch()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAwsManagedPostgresql()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAwsManagedPostgresql() (err error) {
+	err = c.PromptPgUrl()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPgSuperUserName()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPgSuperUserPassword()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPgDbUserName()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPgDbUserPassword()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPgCert()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPgUrl() (err error) {
+	pgUrl, err := c.Prompt.InputStringRegex("AWS Managed RDS PostgreSQL URL:<port>", URL_REQUIRED_PORT_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedRdsInstanceURL = pgUrl
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPgSuperUserName() (err error) {
+	user, err := c.Prompt.InputStringRequired("AWS Managed RDS PostgreSQL Super User Name")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedRdsSuperuserUsername = user
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPgSuperUserPassword() (err error) {
+	pass, err := c.Prompt.InputPassword("AWS Managed RDS PostgreSQL Super User Password")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedRdsSuperuserPassword = pass
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPgDbUserName() (err error) {
+	user, err := c.Prompt.InputStringRequired("AWS Managed RDS PostgreSQL DB User Name")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedRdsDbuserUsername = user
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPgDbUserPassword() (err error) {
+	pass, err := c.Prompt.InputPassword("AWS Managed RDS PostgreSQL DB User Password")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedRdsDbuserPassword = pass
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPgCert() (err error) {
+	awsDefaultCert, err := c.Prompt.Confirm("Do you want to use Default AWS Cert to connect with AWS Managed RDS PosgreSQL URL", "yes", "no")
+	if err != nil {
+		return
+	}
+	if awsDefaultCert {
+		c.Config.InitAws().InitConfigAwsSettings().ManagedRdsCertificate = ""
+		return
+	}
+	certFilePath, err := c.Prompt.InputExistingFilePath("AWS Managed RDS PostgreSQL Certificate File Path")
+	if err != nil {
+		return
+	}
+	certFile, err := c.FileUtils.ReadFile(certFilePath)
+	if err != nil {
+		return
+	}
+	cert := strings.TrimSpace(string(certFile))
+	c.Config.InitAws().InitConfigAwsSettings().ManagedRdsCertificate = cert
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAwsManagedOpenSearch() (err error) {
+	err = c.PromptOsDomainName()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsDomainUrl()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsUserName()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsUserPassword()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsCert()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsSnapshotRoleArn()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsSnapshotUserAccessKeyId()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsSnapshotUserAccessKeySecret()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsDomainName() (err error) {
+	osDomainName, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Domain Name")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedOpensearchDomainName = osDomainName
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsDomainUrl() (err error) {
+	osDomainUrl, err := c.Prompt.InputStringRegex("AWS Managed OpenSearch Domain URL", URL_OPTIONAL_PORT_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedOpensearchDomainURL = osDomainUrl
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsUserName() (err error) {
+	user, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch User Name")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedOpensearchUsername = user
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsUserPassword() (err error) {
+	pass, err := c.Prompt.InputPassword("AWS Managed OpenSearch User Password")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ManagedOpensearchUserPassword = pass
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsCert() (err error) {
+	awsDefaultCert, err := c.Prompt.Confirm("Do you want to use Default AWS Cert to connect with AWS Managed OpenSearch Domain URL", "yes", "no")
+	if err != nil {
+		return
+	}
+	if awsDefaultCert {
+		c.Config.InitAws().InitConfigAwsSettings().ManagedOpensearchCertificate = ""
+		return
+	}
+	certFilePath, err := c.Prompt.InputExistingFilePath("AWS Managed OpenSearch Certificate File Path")
+	if err != nil {
+		return
+	}
+	certFile, err := c.FileUtils.ReadFile(certFilePath)
+	if err != nil {
+		return
+	}
+	cert := strings.TrimSpace(string(certFile))
+	c.Config.InitAws().InitConfigAwsSettings().ManagedOpensearchCertificate = cert
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsSnapshotRoleArn() (err error) {
+	arn, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Snapshot Role ARN")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AwsOsSnapshotRoleArn = arn
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsSnapshotUserAccessKeyId() (err error) {
+	keyId, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Snapshot User Access Key ID")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().OsSnapshotUserAccessKeyID = keyId
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsSnapshotUserAccessKeySecret() (err error) {
+	secret, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Snapshot User Access Key Secret")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().OsSnapshotUserAccessKeySecret = secret
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefManaged() (err error) {
+	err = c.PromptPgChefManaged()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOsChefManaged()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomate() (err error) {
+	err = c.PromptAutomateFqdn()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateLoadBalancerCertArn()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateFqdnRootCa()
+	if err != nil {
+		return
+	}
+
+	c.DefaultAutomateConfigValues()
+
+	err = c.PromptAutomateAdminPassword()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateNodes()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateInstanceType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateVolSize()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateVolType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateVolIops()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServer() (err error) {
+	err = c.PromptChefInfraServerFqdn()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfrServerLoadBalancerCertArn()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerFqdnRootCa()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerNodes()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerInstanceType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerVolSize()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerVolType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerVolIops()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPgChefManaged() (err error) {
+	err = c.PromptPostgresqlNodes()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPostgresqlInstanceType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPostgresqlVolSize()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPostgresqlVolType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptPostgresqlVolIops()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsChefManaged() (err error) {
+	err = c.PromptOpenSearchNodes()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOpenSearchInstanceType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOpenSearchVolSize()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOpenSearchVolType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptOpenSearchVolIops()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) DefaultAutomateConfigValues() {
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.ConfigFile = "configs/automate.toml"
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateFqdn() (err error) {
+	automateFqdn, err := c.Prompt.InputStringRegex("Automate FQDN", FQDN_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.Fqdn = automateFqdn
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateFqdnRootCa() (err error) {
+	automateFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Automate FQDN Root CA File Path")
+	if err != nil {
+		return
+	}
+
+	automateFqdnRootCaFile, err := c.FileUtils.ReadFile(automateFqdnRootCaFilePath)
+	if err != nil {
+		return
+	}
+
+	automateFqdnRootCa := string(automateFqdnRootCaFile)
+	automateFqdnRootCa = strings.TrimSpace(automateFqdnRootCa)
+
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.FqdnRootCA = automateFqdnRootCa
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptHaveCustomCerts(nodeType string) (customCerts bool, err error) {
+	if c.HasCustomCerts {
+		customCerts, err = c.Prompt.Confirm("Do you have custom certs for "+nodeType+" Nodes", "yes", "no")
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptCert(ip, nodeType, certName string) (certVal string, err error) {
+	msgCert := "Provide " + certName + " file path for " + nodeType
+	if ip != "" {
+		msgCert += " Node on IP: " + ip
+	} else {
+		msgCert += " Service"
+	}
+	certFilePath, err := c.Prompt.InputExistingFilePath(msgCert)
+	if err != nil {
+		return
+	}
+	certFile, err := c.FileUtils.ReadFile(certFilePath)
+	if err != nil {
+		return
+	}
+	certVal = string(certFile)
+	certVal = strings.TrimSpace(certVal)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateNodes() (err error) {
+	automateNodeCount, err := c.Prompt.InputIntRange("No. of Automate Nodes", 1, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.InstanceCount = fmt.Sprint(automateNodeCount)
+
+	hasCustomCerts, err := c.PromptHaveCustomCerts("Automate")
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitAutomate().InitConfig().EnableCustomCerts = hasCustomCerts
+
+	if hasCustomCerts {
+		priKey, err := c.PromptCert("", AUTOMATE_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.Automate.Config.PrivateKey = priKey
+
+		pubCert, err := c.PromptCert("", AUTOMATE_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.Automate.Config.PublicKey = pubCert
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateAdminPassword() (err error) {
+	adminPass, err := c.Prompt.InputPasswordRegex("Automate Dashboard Admin User Password", AUTOMATE_ADMIN_PASSWORD_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitAutomate().InitConfig().AdminPassword = adminPass
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateInstanceType() (err error) {
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for Automate", AWS_MACHINE_TYPE_REGEX, "t3.medium")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AutomateServerInstanceType = instanceType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServerFqdn() (err error) {
+	chefInfraServerFqdn, err := c.Prompt.InputStringRegex("Chef Infra Server FQDN", FQDN_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitChefServer().InitConfig()
+	c.Config.ChefServer.Config.ChefServerFqdn = chefInfraServerFqdn
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServerFqdnRootCa() (err error) {
+	chefInfraServerFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Chef Infra Server FQDN Root CA File Path")
+	if err != nil {
+		return
+	}
+
+	chefInfraServerFqdnRootCaFile, err := c.FileUtils.ReadFile(chefInfraServerFqdnRootCaFilePath)
+	if err != nil {
+		return
+	}
+
+	chefInfraServerFqdnRootCa := string(chefInfraServerFqdnRootCaFile)
+
+	chefInfraServerFqdnRootCa = strings.TrimSpace(chefInfraServerFqdnRootCa)
+
+	c.Config.InitChefServer().InitConfig()
+	c.Config.ChefServer.Config.FqdnRootCA = chefInfraServerFqdnRootCa
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServerNodes() (err error) {
+	chefServerNodeCount, err := c.Prompt.InputIntRange("No. of Chef Infra Server Nodes", 1, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitChefServer().InitConfig()
+	c.Config.ChefServer.Config.InstanceCount = fmt.Sprint(chefServerNodeCount)
+
+	hasCustomCerts, err := c.PromptHaveCustomCerts("Chef Infra Server")
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitChefServer().InitConfig().EnableCustomCerts = hasCustomCerts
+
+	if hasCustomCerts {
+		priKey, err := c.PromptCert("", CHEF_INFRA_SERVER_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.ChefServer.Config.PrivateKey = priKey
+
+		pubCert, err := c.PromptCert("", CHEF_INFRA_SERVER_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.ChefServer.Config.PublicKey = pubCert
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServerInstanceType() (err error) {
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for Chef Infra Server", AWS_MACHINE_TYPE_REGEX, "t3.medium")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ChefServerInstanceType = instanceType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPostgresqlInstanceType() (err error) {
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for PostgreSQL", AWS_MACHINE_TYPE_REGEX, "m5.large")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().PostgresqlServerInstanceType = instanceType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOpenSearchInstanceType() (err error) {
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for OpenSearch", AWS_MACHINE_TYPE_REGEX, "m5.large")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().OpensearchServerInstanceType = instanceType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateVolSize() (err error) {
+	size, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume Size for Automate", 200, 200, 16384)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AutomateEbsVolumeSize = fmt.Sprint(size)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServerVolSize() (err error) {
+	size, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume Size for Chef Infra Server", 200, 200, 16384)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ChefEbsVolumeSize = fmt.Sprint(size)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPostgresqlNodes() (err error) {
+	postgresqlNodeCount, err := c.Prompt.InputIntRange("No. of Postgresql Nodes", 3, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitPostgresql().InitConfig()
+	c.Config.Postgresql.Config.InstanceCount = fmt.Sprint(postgresqlNodeCount)
+
+	hasCustomCerts, err := c.PromptHaveCustomCerts("Postgresql")
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitPostgresql().InitConfig().EnableCustomCerts = hasCustomCerts
+
+	if hasCustomCerts {
+		priKey, err := c.PromptCert("", POSTGRESQL_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.Postgresql.Config.PrivateKey = priKey
+
+		pubCert, err := c.PromptCert("", POSTGRESQL_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.Postgresql.Config.PublicKey = pubCert
+
+		rootCa, err := c.PromptCert("", POSTGRESQL_NODETYPE, ROOTCA)
+		if err != nil {
+			return err
+		}
+		c.Config.Postgresql.Config.RootCA = rootCa
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPostgresqlVolSize() (err error) {
+	size, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume Size for PostgreSQL", 200, 200, 16384)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().PostgresqlEbsVolumeSize = fmt.Sprint(size)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOpenSearchNodes() (err error) {
+	opensearchNodeCount, err := c.Prompt.InputIntRange("No. of OpenSearch Nodes", 3, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitOpenSearch().InitConfig()
+	c.Config.Opensearch.Config.InstanceCount = fmt.Sprint(opensearchNodeCount)
+
+	hasCustomCerts, err := c.PromptHaveCustomCerts("OpenSearch")
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitOpenSearch().InitConfig().EnableCustomCerts = hasCustomCerts
+
+	if hasCustomCerts {
+		priKey, err := c.PromptCert("", OPENSEARCH_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.Opensearch.Config.PrivateKey = priKey
+
+		pubCert, err := c.PromptCert("", OPENSEARCH_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.Opensearch.Config.PublicKey = pubCert
+
+		rootCa, err := c.PromptCert("", OPENSEARCH_NODETYPE, ROOTCA)
+		if err != nil {
+			return err
+		}
+		c.Config.Opensearch.Config.RootCA = rootCa
+		adminCert, err := c.PromptCert("", OPENSEARCH_NODETYPE, ADMIN_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.Opensearch.Config.AdminCert = adminCert
+		adminKey, err := c.PromptCert("", OPENSEARCH_NODETYPE, ADMIN_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.Opensearch.Config.AdminKey = adminKey
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOpenSearchVolSize() (err error) {
+	size, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume Size for OpenSearch", 200, 200, 16384)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().OpensearchEbsVolumeSize = fmt.Sprint(size)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateVolIops() (err error) {
+	iops, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume IOPS for Automate", 100, 100, 16000)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AutomateEbsVolumeIops = fmt.Sprint(iops)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServerVolIops() (err error) {
+	iops, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume IOPS for Chef Infra Server", 100, 100, 16000)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ChefEbsVolumeIops = fmt.Sprint(iops)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPostgresqlVolIops() (err error) {
+	iops, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume IOPS for PostgreSQL", 100, 100, 16000)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().PostgresqlEbsVolumeIops = fmt.Sprint(iops)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOpenSearchVolIops() (err error) {
+	iops, err := c.Prompt.InputIntDefaultRange("AWS EBS Volume IOPS for OpenSearch", 100, 100, 16000)
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().OpensearchEbsVolumeIops = fmt.Sprint(iops)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateVolType() (err error) {
+	volType, err := c.Prompt.InputStringRegexDefault("AWS EBS Volume Type for Automate", AWS_VOL_TYPE_REGEX, "gp3")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AutomateEbsVolumeType = volType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfraServerVolType() (err error) {
+	volType, err := c.Prompt.InputStringRegexDefault("AWS EBS Volume IOPS for Chef Infra Server", AWS_VOL_TYPE_REGEX, "gp3")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ChefEbsVolumeType = volType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptPostgresqlVolType() (err error) {
+	volType, err := c.Prompt.InputStringRegexDefault("AWS EBS Volume IOPS for PostgreSQL", AWS_VOL_TYPE_REGEX, "gp3")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().PostgresqlEbsVolumeType = volType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOpenSearchVolType() (err error) {
+	volType, err := c.Prompt.InputStringRegexDefault("AWS EBS Volume IOPS for OpenSearch", AWS_VOL_TYPE_REGEX, "gp3")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().OpensearchEbsVolumeType = volType
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptAutomateLoadBalancerCertArn() (err error) {
+	certArn, err := c.Prompt.InputStringRequired("AWS Load Balancer Cert ARN for Automate")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().AutomateLbCertificateArn = certArn
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptChefInfrServerLoadBalancerCertArn() (err error) {
+	certArn, err := c.Prompt.InputStringRequired("AWS Load Balancer Cert ARN for Chef Infra Server")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().ChefServerLbCertificateArn = certArn
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptLoadBalancerAccessLogs() (err error) {
+	enableAccessLogs, err := c.Prompt.Confirm("Do you want to Enable Access Logs on AWS Load Balancer", "yes", "no")
+	if err != nil {
+		return
+	}
+	c.Config.InitAws().InitConfigAwsSettings().LbAccessLogs = fmt.Sprint(enableAccessLogs)
+	return
+}
+
+func (c *AwsHaProvisionConfig) DefaultAwsValues() {
+	c.Config.InitArchitecture().InitAws()
+	c.Config.Architecture.Aws.SecretsKeyFile = "/hab/a2_deploy_workspace/secrets.key"
+	c.Config.Architecture.Aws.SecretsStoreFile = "/hab/a2_deploy_workspace/secrets.json"
+	c.Config.Architecture.Aws.Architecture = "aws"
+	c.Config.Architecture.Aws.WorkspacePath = "/hab/a2_deploy_workspace"
+}
+
+func (c *AwsHaProvisionConfig) PromptAwsArchitecture() (err error) {
+	c.DefaultAwsValues()
+
+	err = c.PromptSsh()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptCustomCerts()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptSsh() (err error) {
+	sshUser, err := c.PromptSshUser()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptSshGroup(sshUser)
+	if err != nil {
+		return
+	}
+
+	err = c.PromptSshPort()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptSshKey()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptSshUser() (sshUser string, err error) {
+	sshUser, err = c.Prompt.InputStringRegex("SSH User Name", LINUX_USER_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitAws()
+	c.Config.Architecture.Aws.SSHUser = sshUser
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptSshGroup(sshUser string) (err error) {
+	sshGroup, err := c.Prompt.InputStringRegexDefault("SSH Group", LINUX_USER_REGEX, sshUser)
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitAws()
+	c.Config.Architecture.Aws.SSHGroupName = sshGroup
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptSshPort() (err error) {
+	sshPort, err := c.Prompt.InputIntDefaultRange("SSH Port", 22, 0, 65535)
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitAws()
+	c.Config.Architecture.Aws.SSHPort = fmt.Sprint(sshPort)
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptSshKey() (err error) {
+	sshKeyFile, err := c.Prompt.InputExistingFilePath("SSH Private Key File Path [*.pem downloaded for SSH Key Pair from AWS]")
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitAws()
+	c.Config.Architecture.Aws.SSHKeyFile = sshKeyFile
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptCustomCerts() (err error) {
+	noCustomCerts, err := c.Prompt.Confirm("Will you use custom certs for any service like Automate, Chef Infra Server, PostgreSQL, OpenSearch", "no", "yes")
+	if err != nil {
+		return
+	}
+	c.HasCustomCerts = !noCustomCerts
+	c.Config.InitAutomate().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	c.Config.InitChefServer().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	c.Config.InitPostgresql().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	c.Config.InitOpenSearch().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptBackup() (err error) {
+	isBackupNeeded, err := c.Prompt.Confirm("Backup need to be configured during deployment", "yes", "no")
+	if isBackupNeeded {
+		c.Config.InitArchitecture().InitAws().BackupMount = "/mnt/automate_backups"
+		backupConfig := "s3"
+
+		if !c.Config.InitAws().InitConfigAwsSettings().SetupManagedServices {
+			_, backupOption, err1 := c.Prompt.Select("Which backup option will you use", "AWS S3", "EFS")
+			if err1 != nil {
+				return err1
+			}
+			switch backupOption {
+			case "AWS S3":
+				backupConfig = "s3"
+			case "EFS":
+				backupConfig = "efs"
+			}
+		}
+
+		c.Config.InitArchitecture().InitAws().BackupConfig = backupConfig
+
+		if backupConfig == "s3" {
+
+			s3Bucket, err1 := c.Prompt.InputStringRequired("AWS S3 Bucket Name")
+			if err1 != nil {
+				return err1
+			}
+			c.Config.InitArchitecture().InitAws().S3BucketName = s3Bucket
+		}
+
+	}
+	return
+}
+
+func (c *AwsHaProvisionConfig) SetZeroDBNodes() {
+	c.Config.InitOpenSearch().InitConfig()
+	c.Config.Opensearch.Config.InstanceCount = "0"
+
+	c.Config.InitPostgresql().InitConfig()
+	c.Config.Postgresql.Config.InstanceCount = "0"
+}
diff --git a/lib/config/genconfig/awsregions.go b/lib/config/genconfig/awsregions.go
new file mode 100644
index 000000000..5b1f1d38d
--- /dev/null
+++ b/lib/config/genconfig/awsregions.go
@@ -0,0 +1,81 @@
+package genconfig
+
+import (
+	"fmt"
+
+	"github.com/chef/automate/lib/pmt"
+)
+
+type AwsRegions interface {
+	Choose(label string) (regionVal string, err error)
+}
+
+type AwsRegionsImp struct {
+	Prompt pmt.Prompt
+}
+
+func AwsRegionsImpFactory(p pmt.Prompt) *AwsRegionsImp {
+	return &AwsRegionsImp{
+		Prompt: p,
+	}
+}
+
+type awsRegionItem struct {
+	Label string
+	Key   string
+}
+
+var AwsRegionsList = []awsRegionItem{
+	{"US East (Ohio) us-east-2", "us-east-2"},
+	{"US East (N. Virginia) us-east-1", "us-east-1"},
+	{"US West (N. California) us-west-1", "us-west-1"},
+	{"US West (Oregon) us-west-2", "us-west-2"},
+	{"Africa (Cape Town) af-south-1", "af-south-1"},
+	{"Asia Pacific (Hong Kong) ap-east-1", "ap-east-1"},
+	{"Asia Pacific (Hyderabad) ap-south-2", "ap-south-2"},
+	{"Asia Pacific (Jakarta) ap-southeast-3", "ap-southeast-3"},
+	{"Asia Pacific (Melbourne) ap-southeast-4", "ap-southeast-4"},
+	{"Asia Pacific (Mumbai) ap-south-1", "ap-south-1"},
+	{"Asia Pacific (Osaka) ap-northeast-3", "ap-northeast-3"},
+	{"Asia Pacific (Seoul) ap-northeast-2", "ap-northeast-2"},
+	{"Asia Pacific (Singapore) ap-southeast-1", "ap-southeast-1"},
+	{"Asia Pacific (Sydney) ap-southeast-2", "ap-southeast-2"},
+	{"Asia Pacific (Tokyo) ap-northeast-1", "ap-northeast-1"},
+	{"Canada (Central) ca-central-1", "ca-central-1"},
+	{"Europe (Frankfurt) eu-central-1", "eu-central-1"},
+	{"Europe (Ireland) eu-west-1", "eu-west-1"},
+	{"Europe (London) eu-west-2", "eu-west-2"},
+	{"Europe (Milan) eu-south-1", "eu-south-1"},
+	{"Europe (Paris) eu-west-3", "eu-west-3"},
+	{"Europe (Spain) eu-south-2", "eu-south-2"},
+	{"Europe (Stockholm) eu-north-1", "eu-north-1"},
+	{"Europe (Zurich) eu-central-2", "eu-central-2"},
+	{"Middle East (Bahrain) me-south-1", "me-south-1"},
+	{"Middle East (UAE) me-central-1", "me-central-1"},
+	{"South America (São Paulo) sa-east-1", "sa-east-1"},
+	{"AWS GovCloud (US-East) us-gov-east-1", "us-gov-east-1"},
+	{"AWS GovCloud (US-West) us-gov-west-1", "us-gov-west-1"},
+}
+
+func (r *AwsRegionsImp) genListMap() (list []string, findMap map[string]string) {
+	list = []string{}
+	findMap = map[string]string{}
+	for _, v := range AwsRegionsList {
+		k := fmt.Sprintf("%v %v", v.Label, v.Key)
+		list = append(list, k)
+		if _, ok := findMap[k]; !ok {
+			findMap[k] = v.Key
+		}
+	}
+	return
+}
+
+func (r *AwsRegionsImp) Choose(label string) (regionVal string, err error) {
+	awsRegionOptions, findMap := r.genListMap()
+	_, region, err := r.Prompt.SelectSearch(label, awsRegionOptions...)
+	if err != nil {
+		return
+	}
+	regionVal = findMap[region]
+	return
+}
diff --git a/lib/config/genconfig/genconfig.go b/lib/config/genconfig/genconfig.go
new file mode 100644
index 000000000..1e66b206f
--- /dev/null
+++ b/lib/config/genconfig/genconfig.go
@@ -0,0 +1,139 @@
+package genconfig
+
+import (
+	"github.com/chef/automate/lib/pmt"
+)
+
+type TopologyType string
+
+const (
+	TOPOLOGY_TYPE_STANDALONE TopologyType = "Standalone Chef Automate"
+	TOPOLOGY_TYPE_HA         TopologyType = "Chef Automate HA"
+)
+
+type DeployType string
+
+const (
+	DEPLOY_TYPE_ON_PREMISE DeployType = "On-Premise"
+	DEPLOY_TYPE_AWS        DeployType = "AWS"
+)
+
+type ConfigType string
+
+const (
+	CONFIG_TYPE_DEPLOY   ConfigType = "Deployment"
+	CONFIG_TYPE_AUTOMATE ConfigType = "Automate"
+	CONFIG_TYPE_INFRA    ConfigType = "Chef Infra Server"
+	CONFIG_TYPE_PG       ConfigType = "PostgreSQL"
+	CONFIG_TYPE_OS       ConfigType = "OpenSearch"
+)
+
+type GenConfig interface {
+	GenConfigWithPrompts() (err error)
+	Toml() (tomlBytes []byte, err error)
+	TomlSave(filepath string, err error)
+}
+
+type GenConfigImp struct {
+	Prompt pmt.Prompt
+	Config IConfig
+}
+
+func GenConfigImpFactory(p pmt.Prompt) *GenConfigImp {
+	return &GenConfigImp{
+		Prompt: p,
+	}
+}
+
+func (g *GenConfigImp) GenConfigWithPrompts() (err error) {
+	return g.TopologyFlow()
+}
+
+func (g *GenConfigImp) Toml() (tomlBytes []byte, err error) {
+	return g.Config.Toml()
+}
+
+func (g *GenConfigImp) TopologyFlow() (err error) {
+	_, topologyType, err := g.Prompt.Select("Choose Topology", string(TOPOLOGY_TYPE_HA)) //, string(TOPOLOGY_TYPE_STANDALONE))
+	if err != nil {
+		return
+	}
+	switch topologyType {
+	case string(TOPOLOGY_TYPE_HA):
+		return g.HaFlow()
+	case string(TOPOLOGY_TYPE_STANDALONE):
+		return g.StandaloneFlow()
+	}
+	return
+}
+
+func (g *GenConfigImp) StandaloneFlow() (err error) {
+	g.Config = AutomateStandaloneConfigFactory()
+	return
+}
+
+func (g *GenConfigImp) HaFlow() (err error) {
+	_, deployType, err := g.Prompt.Select("Choose type of Deployment Type", string(DEPLOY_TYPE_ON_PREMISE), string(DEPLOY_TYPE_AWS))
+	if err != nil {
+		return
+	}
+	switch deployType {
+	case string(DEPLOY_TYPE_ON_PREMISE):
+		return g.OnPremFlow()
+	case string(DEPLOY_TYPE_AWS):
+		return g.AwsHaProvisionFlow()
+	}
+	return
+}
+
+func (g *GenConfigImp) OnPremFlow() (err error) {
+	_, configType, err := g.Prompt.Select("Choose config type",
+		string(CONFIG_TYPE_DEPLOY))
+	//, string(CONFIG_TYPE_AUTOMATE), string(CONFIG_TYPE_INFRA), string(CONFIG_TYPE_PG), string(CONFIG_TYPE_OS))
+	if err != nil {
+		return
+	}
+	switch configType {
+	case string(CONFIG_TYPE_DEPLOY):
+		return g.HaDeployFlow()
+	case string(CONFIG_TYPE_AUTOMATE):
+		return g.HaAutomateFlow()
+	case string(CONFIG_TYPE_INFRA):
+		return g.HaInfraFlow()
+	case string(CONFIG_TYPE_PG):
+		return g.HaPgFlow()
+	case string(CONFIG_TYPE_OS):
+		return g.HaOsFlow()
+	}
+	return
+}
+
+func (g *GenConfigImp) HaDeployFlow() (err error) {
+	g.Config = HaDeployConfigFactory(g.Prompt)
+	return g.Config.Prompts()
+}
+
+func (g *GenConfigImp) HaAutomateFlow() (err error) {
+	g.Config = AutomateHaConfigFactory()
+	return g.Config.Prompts()
+}
+
+func (g *GenConfigImp) HaInfraFlow() (err error) {
+	g.Config = InfraHaConfigFactory()
+	return g.Config.Prompts()
+}
+
+func (g *GenConfigImp) HaPgFlow() (err error) {
+	g.Config = PgHaConfigFactory()
+	return g.Config.Prompts()
+}
+
+func (g *GenConfigImp) HaOsFlow() (err error) {
+	g.Config = OsHaConfigFactory()
+	return g.Config.Prompts()
+}
+
+func (g *GenConfigImp) AwsHaProvisionFlow() (err error) {
+	g.Config = AwsHaProvisionConfigFactory(g.Prompt)
+	return g.Config.Prompts()
+}
diff --git a/lib/config/genconfig/hadeployconfig.go b/lib/config/genconfig/hadeployconfig.go
new file mode 100644
index 000000000..00979010d
--- /dev/null
+++ b/lib/config/genconfig/hadeployconfig.go
@@ -0,0 +1,1102 @@
+package genconfig
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/pmt"
+	"github.com/chef/automate/lib/toml"
+)
+
+const (
+	PROMPT_NODE_IP = "%v. Node IP address"
+	AWS_S3         = "AWS S3"
+	MINIO          = "Minio"
+	OBJECT_STORE   = "Object Store"
+	FILE_SYSTEM    = "File System"
+	NFS            = "NFS"
+	EFS            = "EFS"
+)
+
+type HaDeployConfigGen struct {
+	Prompt         pmt.Prompt `toml:"-"`
+	Config         *config.HaDeployConfig
+	HasCustomCerts bool                `toml:"-"`
+	FileUtils      fileutils.FileUtils `toml:"-"`
+}
+
+func HaDeployConfigFactory(p pmt.Prompt) *HaDeployConfigGen {
+	return &HaDeployConfigGen{
+		Prompt:    p,
+		Config:    &config.HaDeployConfig{},
+		FileUtils: fileutils.NewFileSystemUtils(),
+	}
+}
+
+func (c *HaDeployConfigGen) Toml() (tomlBytes []byte, err error) {
+	return toml.Marshal(c.Config)
+}
+
+func (c *HaDeployConfigGen) Prompts() (err error) {
+	c.DefaultExistingInfraValues()
+
+	err = c.PromptSsh()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptCustomCerts()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomate()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefServer()
+	if err != nil {
+		return
+	}
+
+	isExternalDb, err := c.PromptExternalDb()
+	if err != nil {
+		return
+	}
+	if isExternalDb {
+		c.SetZeroDBNodes()
+	} else {
+		err = c.PromptOpenSearch()
+		if err != nil {
+			return
+		}
+
+		err = c.PromptPostgresql()
+		if err != nil {
+			return
+		}
+	}
+
+	err = c.PromptBackup()
+	if err != nil {
+		return
+	}
+
+	err = c.Config.Verify()
+	if err != nil {
+		fmt.Println("ERRORs in Validation:\n", err)
+		return nil
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptCustomCerts() (err error) {
+	noCustomCerts, err := c.Prompt.Confirm("Will you use custom certs for any service like Automate, Chef Infra Server, PostgreSQL, OpenSearch", "no", "yes")
+	if err != nil {
+		return
+	}
+	c.HasCustomCerts = !noCustomCerts
+	c.Config.InitAutomate().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	c.Config.InitChefServer().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	c.Config.InitPostgresql().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	c.Config.InitOpenSearch().InitConfig().EnableCustomCerts = c.HasCustomCerts
+	return
+}
+
+func (c *HaDeployConfigGen) SetZeroDBNodes() {
+	c.Config.InitOpenSearch().InitConfig()
+	c.Config.Opensearch.Config.InstanceCount = "0"
+
+	c.Config.InitPostgresql().InitConfig()
+	c.Config.Postgresql.Config.InstanceCount = "0"
+}
+
+func (c *HaDeployConfigGen) PromptExternalDb() (hasExternalDb bool, err error) {
+	hasExternalDb, err = c.Prompt.Confirm("Are you going to use External Databases, like AWS RDS and AWS OpenSearch", "yes", "no")
+	if err != nil {
+		return
+	}
+	if !hasExternalDb {
+		return
+	}
+
+	err = c.PromptExternalDbType()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalOpenSearch()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalPostgresql()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalDbType() (err error) {
+	isAWSManaged, err := c.Prompt.Confirm("Type of External DB you will use", "AWS Managed", "Self Managed")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase()
+	if isAWSManaged {
+		c.Config.External.Database.Type = "aws"
+	} else {
+		c.Config.External.Database.Type = "self-managed"
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearch() (err error) {
+	err = c.PromptExternalOpenSearchDomainName()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalOpenSearchDomainUrl()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalOpenSearchUser()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalOpenSearchPassword()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalOpenSearchRootCert()
+	if err != nil {
+		return
+	}
+
+	if c.Config.External.Database.Type == "aws" {
+		err = c.PromptExternalOpenSearchAwsSnapshotArn()
+		if err != nil {
+			return
+		}
+
+		err = c.PromptExternalOpenSearchAwsSnapshotUserAccessKey()
+		if err != nil {
+			return
+		}
+
+		err = c.PromptExternalOpenSearchAwsSnapshotUserAccessSecret()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalPostgresql() (err error) {
+	err = c.PromptExternalPostgresqlInstanceUrl()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalPostgresqlSuperUser()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalPostgresqlSuperUserPassword()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalPostgresqlDbUser()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalPostgresqlDbUserPassword()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptExternalPostgresqlRootCert()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) GetExternalPgType() (dbType string) {
+	c.Config.InitExternal().InitDatabase()
+	if c.Config.External.Database.Type == "aws" {
+		dbType = "AWS RDS Posgresql"
+	} else {
+		dbType = "Self Managed Postgresql"
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) GetExternalOsType() (dbType string) {
+	c.Config.InitExternal().InitDatabase()
+	if c.Config.External.Database.Type == "aws" {
+		dbType = "AWS OpenSearch"
+	} else {
+		dbType = "Self Managed OpenSearch"
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalPostgresqlInstanceUrl() (err error) {
+	pgUrl, err := c.Prompt.InputStringRegex(c.GetExternalPgType()+" URL:<port>", URL_REQUIRED_PORT_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitPostgresql()
+	c.Config.External.Database.PostgreSQL.InstanceURL = pgUrl
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalPostgresqlSuperUser() (err error) {
+	user, err := c.Prompt.InputStringRequired(c.GetExternalPgType() + " Super Username")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitPostgresql()
+	c.Config.External.Database.PostgreSQL.SuperuserUsername = user
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalPostgresqlSuperUserPassword() (err error) {
+	pass, err := c.Prompt.InputPassword(c.GetExternalPgType() + " Super User Password")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitPostgresql()
+	c.Config.External.Database.PostgreSQL.SuperuserPassword = pass
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalPostgresqlDbUser() (err error) {
+	user, err := c.Prompt.InputStringRequired(c.GetExternalPgType() + " Db Username")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitPostgresql()
+	c.Config.External.Database.PostgreSQL.DbuserUsername = user
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalPostgresqlDbUserPassword() (err error) {
+	pass, err := c.Prompt.InputPassword(c.GetExternalPgType() + " DB User Password")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitPostgresql()
+	c.Config.External.Database.PostgreSQL.DbuserPassword = pass
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalPostgresqlRootCert() (err error) {
+	if c.Config.External.Database.Type == "aws" {
+		awsDefaultCert := false
+		awsDefaultCert, err = c.Prompt.Confirm("Do you want to use Default AWS Cert to connect with AWS Managed RDS PostgreSQL URL", "yes", "no")
+		if err != nil {
+			return
+		}
+		if awsDefaultCert {
+			c.Config.External.Database.PostgreSQL.PostgresqlRootCert = ""
+			return
+		}
+	}
+	rootCertFilePath, err := c.Prompt.InputExistingFilePath(c.GetExternalPgType() + " Root Cert File Path")
+	if err != nil {
+		return
+	}
+
+	rootCertFile, err := c.FileUtils.ReadFile(rootCertFilePath)
+	if err != nil {
+		return
+	}
+
+	rootCert := string(rootCertFile)
+	rootCert = strings.TrimSpace(rootCert)
+
+	c.Config.InitExternal().InitDatabase().InitPostgresql()
+	c.Config.External.Database.PostgreSQL.PostgresqlRootCert = rootCert
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchDomainName() (err error) {
+	domainName, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Domain Name")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitOpenSearch()
+	c.Config.External.Database.OpenSearch.OpensearchDomainName = domainName
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchDomainUrl() (err error) {
+	domainUrl, err := c.Prompt.InputStringRegex(c.GetExternalOsType()+" Domain URL", URL_OPTIONAL_PORT_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitOpenSearch()
+	c.Config.External.Database.OpenSearch.OpensearchDomainURL = domainUrl
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchUser() (err error) {
+	user, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Username")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitOpenSearch()
+	c.Config.External.Database.OpenSearch.OpensearchUsername = user
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchPassword() (err error) {
+	pass, err := c.Prompt.InputPassword(c.GetExternalOsType() + " User Password")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitOpenSearch()
+	c.Config.External.Database.OpenSearch.OpensearchUserPassword = pass
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchRootCert() (err error) {
+	if c.Config.External.Database.Type == "aws" {
+		awsDefaultCert := false
+		awsDefaultCert, err = c.Prompt.Confirm("Do you want to use Default AWS Cert to connect with AWS Managed OpenSearch Domain URL", "yes", "no")
+		if err != nil {
+			return
+		}
+		if awsDefaultCert {
+			c.Config.External.Database.OpenSearch.OpensearchRootCert = ""
+			return
+		}
+	}
+	rootCertFilePath, err := c.Prompt.InputExistingFilePath(c.GetExternalOsType() + " Root Cert File Path")
+	if err != nil {
+		return
+	}
+
+	rootCertFile, err := c.FileUtils.ReadFile(rootCertFilePath)
+	if err != nil {
+		return
+	}
+
+	rootCert := string(rootCertFile)
+	rootCert = strings.TrimSpace(rootCert)
+
+	c.Config.InitExternal().InitDatabase().InitOpenSearch()
+	c.Config.External.Database.OpenSearch.OpensearchRootCert = rootCert
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchAwsSnapshotArn() (err error) {
+	arn, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Snapshot ARN")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitOpenSearch().InitOpenSearchAws()
+	c.Config.External.Database.OpenSearch.Aws.AwsOsSnapshotRoleArn = arn
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchAwsSnapshotUserAccessKey() (err error) {
+	key, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Snapshot User Access Key")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitOpenSearch().InitOpenSearchAws()
+	c.Config.External.Database.OpenSearch.Aws.OsSnapshotUserAccessKeyID = key
+	return
+}
+
+func (c *HaDeployConfigGen) PromptExternalOpenSearchAwsSnapshotUserAccessSecret() (err error) {
+	secret, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Snapshot User Access Secret")
+	if err != nil {
+		return
+	}
+	c.Config.InitExternal().InitDatabase().InitOpenSearch().InitOpenSearchAws()
+	c.Config.External.Database.OpenSearch.Aws.OsSnapshotUserAccessKeySecret = secret
+	return
+}
+
+func (c *HaDeployConfigGen) DefaultAutomateConfigValues() {
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.ConfigFile = "configs/automate.toml"
+}
+
+func (c *HaDeployConfigGen) PromptAutomate() (err error) {
+	err = c.PromptAutomateFqdn()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateFqdnRootCa()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptAutomateAdminPassword()
+	if err != nil {
+		return
+	}
+
+	c.DefaultAutomateConfigValues()
+
+	err = c.PromptAutomateNodes()
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+func (c *HaDeployConfigGen) PromptAutomateAdminPassword() (err error) {
+	adminPass, err := c.Prompt.InputPasswordRegex("Automate Dashboard Admin User Password", AUTOMATE_ADMIN_PASSWORD_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitAutomate().InitConfig().AdminPassword = adminPass
+	return
+}
+
+func (c *HaDeployConfigGen) PromptChefServer() (err error) {
+	err = c.PromptChefInfraServerFqdn()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerFqdnRootCa()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptChefInfraServerNodes()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptOpenSearch() (err error) {
+	err = c.PromptOpenSearchNodes()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptPostgresql() (err error) {
+	err = c.PromptPostgresqlNodes()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptChefInfraServerNodes() (err error) {
+	chefServerNodeCount, err := c.Prompt.InputIntRange("No. of Chef Infra Server Nodes", 1, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitChefServer().InitConfig()
+	c.Config.ChefServer.Config.InstanceCount = fmt.Sprint(chefServerNodeCount)
+
+	hasCustomCerts, hasCustomCertsPerNode, err := c.PromptHaveCustomCerts(CHEF_INFRA_SERVER_NODETYPE)
+	if err != nil {
+		return err
+	}
+
+	c.Config.ChefServer.Config.EnableCustomCerts = hasCustomCerts
+
+	err = c.PromptChefInfraServerPriPubCerts(hasCustomCerts, hasCustomCertsPerNode)
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitExistingInfra().InitConfig()
+	for i := 0; i < chefServerNodeCount; i++ {
+		err = c.PromptChefInfraServerNodeIpCerts(i, hasCustomCerts, hasCustomCertsPerNode)
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptChefInfraServerPriPubCerts(hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	if hasCustomCerts && !hasCustomCertsPerNode {
+		priKey, err := c.PromptCert("", CHEF_INFRA_SERVER_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.ChefServer.Config.PrivateKey = priKey
+
+		pubCert, err := c.PromptCert("", CHEF_INFRA_SERVER_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.ChefServer.Config.PublicKey = pubCert
+	}
+	if hasCustomCerts && hasCustomCertsPerNode {
+		c.Config.ChefServer.Config.InitCertsByIP()
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptChefInfraServerNodeIpCerts(i int, hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	ip, err := c.PromptNodeIp(fmt.Sprintf(PROMPT_NODE_IP, i+1))
+	if err != nil {
+		return err
+	}
+	c.Config.ExistingInfra.Config.ChefServerPrivateIps = append(c.Config.ExistingInfra.Config.ChefServerPrivateIps, ip)
+	if hasCustomCerts && hasCustomCertsPerNode {
+		priKey, err := c.PromptCert(ip, CHEF_INFRA_SERVER_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+
+		pubCert, err := c.PromptCert(ip, CHEF_INFRA_SERVER_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+
+		nodeCert := config.CertByIP{
+			IP:         ip,
+			PrivateKey: priKey,
+			PublicKey:  pubCert,
+		}
+		*c.Config.ChefServer.Config.CertsByIP = append(*c.Config.ChefServer.Config.CertsByIP, nodeCert)
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptOpenSearchNodes() (err error) {
+	opensearchNodeCount, err := c.Prompt.InputIntRange("No. of OpenSearch Nodes", 3, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitOpenSearch().InitConfig()
+	c.Config.Opensearch.Config.InstanceCount = fmt.Sprint(opensearchNodeCount)
+
+	hasCustomCerts, hasCustomCertsPerNode, err := c.PromptHaveCustomCerts(OPENSEARCH_NODETYPE)
+	if err != nil {
+		return err
+	}
+
+	c.Config.Opensearch.Config.EnableCustomCerts = hasCustomCerts
+
+	err = c.PromptOpenSearchPubPriCerts(hasCustomCerts, hasCustomCertsPerNode)
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitExistingInfra().InitConfig()
+	for i := 0; i < opensearchNodeCount; i++ {
+		err := c.PromptOpenSearchNodeIpCerts(i, hasCustomCerts, hasCustomCertsPerNode)
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptOpenSearchNodeIpCerts(i int, hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	ip, err1 := c.PromptNodeIp(fmt.Sprintf(PROMPT_NODE_IP, i+1))
+	if err1 != nil {
+		return err1
+	}
+	c.Config.ExistingInfra.Config.OpensearchPrivateIps = append(c.Config.ExistingInfra.Config.OpensearchPrivateIps, ip)
+	if hasCustomCerts && hasCustomCertsPerNode {
+		priKey, err := c.PromptCert(ip, OPENSEARCH_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+
+		pubCert, err := c.PromptCert(ip, OPENSEARCH_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+
+		nodeCert := config.CertByIP{
+			IP:         ip,
+			PrivateKey: priKey,
+			PublicKey:  pubCert,
+		}
+		*c.Config.Opensearch.Config.CertsByIP = append(*c.Config.Opensearch.Config.CertsByIP, nodeCert)
+	}
+	return
+}
+
+func (haconfiggen *HaDeployConfigGen) PromptOpenSearchPubPriCerts(hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	if hasCustomCerts && !hasCustomCertsPerNode {
+		priKey, err := haconfiggen.PromptCert("", OPENSEARCH_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		haconfiggen.Config.InitOpenSearch().InitConfig().PrivateKey = priKey
+
+		pubCert, err := haconfiggen.PromptCert("", OPENSEARCH_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		haconfiggen.Config.InitOpenSearch().InitConfig().PublicKey = pubCert
+	}
+
+	if hasCustomCerts {
+		rootCa, err := haconfiggen.PromptCert("", OPENSEARCH_NODETYPE, ROOTCA)
+		if err != nil {
+			return err
+		}
+		haconfiggen.Config.Opensearch.Config.RootCA = rootCa
+		adminCert, err := haconfiggen.PromptCert("", OPENSEARCH_NODETYPE, ADMIN_CERT)
+		if err != nil {
+			return err
+		}
+		haconfiggen.Config.Opensearch.Config.AdminCert = adminCert
+		adminKey, err := haconfiggen.PromptCert("", OPENSEARCH_NODETYPE, ADMIN_KEY)
+		if err != nil {
+			return err
+		}
+		haconfiggen.Config.Opensearch.Config.AdminKey = adminKey
+	}
+
+	if hasCustomCerts && hasCustomCertsPerNode {
+		haconfiggen.Config.Opensearch.Config.InitCertsByIP()
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptPostgresqlNodes() (err error) {
+	postgresqlNodeCount, err := c.Prompt.InputIntRange("No. of Postgresql Nodes", 3, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitPostgresql().InitConfig()
+	c.Config.Postgresql.Config.InstanceCount = fmt.Sprint(postgresqlNodeCount)
+
+	hasCustomCerts, hasCustomCertsPerNode, err := c.PromptHaveCustomCerts(POSTGRESQL_NODETYPE)
+	if err != nil {
+		return err
+	}
+
+	c.Config.Postgresql.Config.EnableCustomCerts = hasCustomCerts
+
+	err = c.PromptPostgresqlPubPriCerts(hasCustomCerts, hasCustomCertsPerNode)
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitExistingInfra().InitConfig()
+	for i := 0; i < postgresqlNodeCount; i++ {
+		err = c.PromptPostgresqlIpCerts(i, hasCustomCerts, hasCustomCertsPerNode)
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptPostgresqlIpCerts(i int, hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	ip, err1 := c.PromptNodeIp(fmt.Sprintf(PROMPT_NODE_IP, i+1))
+	if err1 != nil {
+		return err1
+	}
+	c.Config.ExistingInfra.Config.PostgresqlPrivateIps = append(c.Config.ExistingInfra.Config.PostgresqlPrivateIps, ip)
+	if hasCustomCerts && hasCustomCertsPerNode {
+		priKey, err := c.PromptCert(ip, POSTGRESQL_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+
+		pubCert, err := c.PromptCert(ip, POSTGRESQL_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+
+		nodeCert := config.CertByIP{
+			IP:         ip,
+			PrivateKey: priKey,
+			PublicKey:  pubCert,
+		}
+		*c.Config.Postgresql.Config.CertsByIP = append(*c.Config.Postgresql.Config.CertsByIP, nodeCert)
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptPostgresqlPubPriCerts(hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	if hasCustomCerts && !hasCustomCertsPerNode {
+		priKey, err := c.PromptCert("", POSTGRESQL_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.Postgresql.Config.PrivateKey = priKey
+
+		pubCert, err := c.PromptCert("", POSTGRESQL_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.Postgresql.Config.PublicKey = pubCert
+	}
+
+	if hasCustomCerts {
+		rootCa, err := c.PromptCert("", POSTGRESQL_NODETYPE, ROOTCA)
+		if err != nil {
+			return err
+		}
+		c.Config.Postgresql.Config.RootCA = rootCa
+	}
+
+	if hasCustomCerts && hasCustomCertsPerNode {
+		c.Config.Postgresql.Config.InitCertsByIP()
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptNodeIp(msg string) (ip string, err error) {
+	ip, err = c.Prompt.InputStringRegex(msg, IP_REGEX)
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptHaveCustomCerts(nodeType string) (customCerts, customCertsPerNode bool, err error) {
+	if c.HasCustomCerts {
+		customCerts, err = c.Prompt.Confirm("Do you have custom certs for "+nodeType+" Nodes", "yes", "no")
+		if err != nil {
+			return
+		}
+
+		if customCerts {
+			commonCerts, err1 := c.Prompt.Confirm("Do each "+nodeType+" Node have same Certs", "yes", "no")
+			if err1 != nil {
+				return commonCerts, customCertsPerNode, err1
+			}
+			customCertsPerNode = !commonCerts
+		}
+	}
+	return
+}
+func (c *HaDeployConfigGen) PromptCert(ip, nodeType, certName string) (certVal string, err error) {
+	msgCert := "Provide " + certName + " file path for " + nodeType
+	if ip != "" {
+		msgCert += " Node on IP: " + ip
+	} else {
+		msgCert += " Service"
+	}
+	certFilePath, err := c.Prompt.InputExistingFilePath(msgCert)
+	if err != nil {
+		return
+	}
+	certFile, err := c.FileUtils.ReadFile(certFilePath)
+	if err != nil {
+		return
+	}
+	certVal = string(certFile)
+	certVal = strings.TrimSpace(certVal)
+	return
+}
+
+func (c *HaDeployConfigGen) PromptAutomateNodes() (err error) {
+	automateNodeCount, err := c.Prompt.InputIntRange("No. of Automate Nodes", 1, 100)
+	if err != nil {
+		return
+	}
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.InstanceCount = fmt.Sprint(automateNodeCount)
+
+	hasCustomCerts, hasCustomCertsPerNode, err := c.PromptHaveCustomCerts(AUTOMATE_NODETYPE)
+	if err != nil {
+		return err
+	}
+
+	c.Config.Automate.Config.EnableCustomCerts = hasCustomCerts
+
+	err = c.PromptAutomateCerts(hasCustomCerts, hasCustomCertsPerNode)
+	if err != nil {
+		return err
+	}
+
+	c.Config.InitExistingInfra().InitConfig()
+	for i := 0; i < automateNodeCount; i++ {
+		err = c.PromptAutomateIpCerts(i, hasCustomCerts, hasCustomCertsPerNode)
+		if err != nil {
+			return err
+		}
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptAutomateIpCerts(i int, hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	ip, err1 := c.PromptNodeIp(fmt.Sprintf(PROMPT_NODE_IP, i+1))
+	if err1 != nil {
+		return err1
+	}
+	c.Config.ExistingInfra.Config.AutomatePrivateIps = append(c.Config.ExistingInfra.Config.AutomatePrivateIps, ip)
+	if hasCustomCerts && hasCustomCertsPerNode {
+		priKey, err := c.PromptCert(ip, AUTOMATE_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+
+		pubCert, err := c.PromptCert(ip, AUTOMATE_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+
+		nodeCert := config.CertByIP{
+			IP:         ip,
+			PrivateKey: priKey,
+			PublicKey:  pubCert,
+		}
+		*c.Config.Automate.Config.CertsByIP = append(*c.Config.Automate.Config.CertsByIP, nodeCert)
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptAutomateCerts(hasCustomCerts, hasCustomCertsPerNode bool) (err error) {
+	if hasCustomCerts && !hasCustomCertsPerNode {
+		priKey, err := c.PromptCert("", AUTOMATE_NODETYPE, PRI_KEY)
+		if err != nil {
+			return err
+		}
+		c.Config.Automate.Config.PrivateKey = priKey
+
+		pubCert, err := c.PromptCert("", AUTOMATE_NODETYPE, PUB_CERT)
+		if err != nil {
+			return err
+		}
+		c.Config.Automate.Config.PublicKey = pubCert
+	}
+
+	if hasCustomCerts && hasCustomCertsPerNode {
+		c.Config.Automate.Config.InitCertsByIP()
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptChefInfraServerFqdn() (err error) {
+	chefInfraServerFqdn, err := c.Prompt.InputStringRegex("Chef Infra Server FQDN", FQDN_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitChefServer().InitConfig()
+	c.Config.ChefServer.Config.ChefServerFqdn = chefInfraServerFqdn
+	return
+}
+
+func (c *HaDeployConfigGen) PromptChefInfraServerFqdnRootCa() (err error) {
+	chefInfraServerFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Chef Infra Server FQDN Root CA File Path")
+	if err != nil {
+		return
+	}
+
+	chefInfraServerFqdnRootCaFile, err := c.FileUtils.ReadFile(chefInfraServerFqdnRootCaFilePath)
+	if err != nil {
+		return
+	}
+
+	chefInfraServerFqdnRootCa := string(chefInfraServerFqdnRootCaFile)
+	chefInfraServerFqdnRootCa = strings.TrimSpace(chefInfraServerFqdnRootCa)
+
+	c.Config.InitChefServer().InitConfig()
+	c.Config.ChefServer.Config.FqdnRootCA = chefInfraServerFqdnRootCa
+	return
+}
+
+func (c *HaDeployConfigGen) PromptAutomateFqdn() (err error) {
+	automateFqdn, err := c.Prompt.InputStringRegex("Automate FQDN", FQDN_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.Fqdn = automateFqdn
+	return
+}
+
+func (c *HaDeployConfigGen) PromptAutomateFqdnRootCa() (err error) {
+	automateFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Automate FQDN Root CA File Path")
+	if err != nil {
+		return
+	}
+
+	automateFqdnRootCaFile, err := c.FileUtils.ReadFile(automateFqdnRootCaFilePath)
+	if err != nil {
+		return
+	}
+
+	automateFqdnRootCa := string(automateFqdnRootCaFile)
+	automateFqdnRootCa = strings.TrimSpace(automateFqdnRootCa)
+
+	c.Config.InitAutomate().InitConfig()
+	c.Config.Automate.Config.FqdnRootCA = automateFqdnRootCa
+	return
+}
+
+func (c *HaDeployConfigGen) PromptSshUser() (sshUser string, err error) {
+	sshUser, err = c.Prompt.InputStringRegex("SSH User Name", LINUX_USER_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitExistingInfra()
+	c.Config.Architecture.ExistingInfra.SSHUser = sshUser
+	return
+}
+
+func (c *HaDeployConfigGen) PromptSshGroup(sshUser string) (err error) {
+	sshGroup, err := c.Prompt.InputStringRegexDefault("SSH Group", LINUX_USER_REGEX, sshUser)
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitExistingInfra()
+	c.Config.Architecture.ExistingInfra.SSHGroupName = sshGroup
+	return
+}
+
+func (c *HaDeployConfigGen) PromptSshPort() (err error) {
+	sshPort, err := c.Prompt.InputIntDefaultRange("SSH Port", 22, 0, 65535)
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitExistingInfra()
+	c.Config.Architecture.ExistingInfra.SSHPort = fmt.Sprint(sshPort)
+	return
+}
+
+func (c *HaDeployConfigGen) PromptSshKey() (err error) {
+	sshKeyFile, err := c.Prompt.InputExistingFilePathDefault("SSH Key File", "~/.ssh/id_rsa")
+	if err != nil {
+		return
+	}
+	c.Config.InitArchitecture().InitExistingInfra()
+	c.Config.Architecture.ExistingInfra.SSHKeyFile = sshKeyFile
+	return
+}
+
+func (c *HaDeployConfigGen) PromptSsh() (err error) {
+	sshUser, err := c.PromptSshUser()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptSshGroup(sshUser)
+	if err != nil {
+		return
+	}
+
+	err = c.PromptSshPort()
+	if err != nil {
+		return
+	}
+
+	err = c.PromptSshKey()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) DefaultExistingInfraValues() {
+	c.Config.InitArchitecture().InitExistingInfra()
+	c.Config.Architecture.ExistingInfra.SecretsKeyFile = "/hab/a2_deploy_workspace/secrets.key"
+	c.Config.Architecture.ExistingInfra.SecretsStoreFile = "/hab/a2_deploy_workspace/secrets.json"
+	c.Config.Architecture.ExistingInfra.Architecture = "existing_nodes"
+	c.Config.Architecture.ExistingInfra.WorkspacePath = "/hab/a2_deploy_workspace"
+}
+
+func (c *HaDeployConfigGen) PromptBackup() (err error) {
+	isBackupNeeded, err := c.Prompt.Confirm("Backup need to be configured during deployment", "yes", "no")
+	if isBackupNeeded {
+		c.Config.InitArchitecture().InitExistingInfra().BackupMount = "/mnt/automate_backups"
+
+		_, backupOption, err1 := c.Prompt.Select("Which backup option will you use", AWS_S3, MINIO, OBJECT_STORE, FILE_SYSTEM, NFS, EFS)
+		if err1 != nil {
+			return err1
+		}
+		backupConfig := ""
+		switch backupOption {
+		case AWS_S3, MINIO, OBJECT_STORE:
+			backupConfig = "object_storage"
+		case FILE_SYSTEM, NFS, EFS:
+			backupConfig = "file_system"
+		}
+		c.Config.Architecture.ExistingInfra.BackupConfig = backupConfig
+
+		if backupConfig == "object_storage" {
+			err1 := c.PromptObjectStorageSettings(backupOption)
+			if err1 != nil {
+				return err1
+			}
+		} else if backupConfig == "file_system" {
+			backupMountLoc, err1 := c.Prompt.InputStringRegexDefault("Backup Mount Location", DIR_PATH_REGEX, "/mnt/automate_backups")
+			if err1 != nil {
+				return err1
+			}
+			c.Config.Architecture.ExistingInfra.BackupMount = backupMountLoc
+		}
+	}
+	return
+}
+
+func (c *HaDeployConfigGen) PromptObjectStorageSettings(backupOption string) (err error) {
+
+	bucketName, err := c.Prompt.InputStringRegex("Backup bucket name", BUCKET_NAME_REGEX)
+	if err != nil {
+		return
+	}
+	c.Config.InitObjectStorage().InitConfig().BucketName = bucketName
+
+	if backupOption == AWS_S3 {
+		accessKey, err1 := c.Prompt.InputStringRegex("AWS Access Key ID for bucket", AWS_ACCESS_KEY_ID_REGEX)
+		if err1 != nil {
+			return err1
+		}
+		c.Config.ObjectStorage.Config.AccessKey = accessKey
+
+		secretKey, err1 := c.Prompt.InputStringRegex("AWS Access Key Secret for bucket", AWS_ACCESS_KEY_SECRET_REGEX)
+		if err1 != nil {
+			return err1
+		}
+		c.Config.ObjectStorage.Config.SecretKey = secretKey
+		c.Config.ObjectStorage.Config.Endpoint = "https://s3.amazonaws.com"
+
+		awsRegions := AwsRegionsImpFactory(c.Prompt)
+		bucketRegion, err1 := awsRegions.Choose("AWS Region of bucket")
+		if err1 != nil {
+			return err1
+		}
+		c.Config.ObjectStorage.Config.Region = bucketRegion
+	} else {
+		accessKey, err1 := c.Prompt.InputStringRegex("Access Key ID for bucket", ACCESS_KEY_ID_REGEX)
+		if err1 != nil {
+			return err1
+		}
+		c.Config.ObjectStorage.Config.AccessKey = accessKey
+
+		secretKey, err1 := c.Prompt.InputStringRegex("Access Key Secret for bucket", ACCESS_KEY_SECRET_REGEX)
+		if err1 != nil {
+			return err1
+		}
+		c.Config.ObjectStorage.Config.SecretKey = secretKey
+		bucketEndpoint, err1 := c.Prompt.InputStringRegex("Endpoint for bucket", ENDPOINT_URL)
+		if err1 != nil {
+			return err1
+		}
+		c.Config.ObjectStorage.Config.Endpoint = bucketEndpoint
+	}
+	return
+}
diff --git a/lib/config/genconfig/iconfig.go b/lib/config/genconfig/iconfig.go
new file mode 100644
index 000000000..1eb4f77f0
--- /dev/null
+++ b/lib/config/genconfig/iconfig.go
@@ -0,0 +1,6 @@
+package genconfig
+
+type IConfig interface {
+	Toml() (tomlBytes []byte, err error)
+	Prompts() (err error)
+}
diff --git a/lib/config/genconfig/infrahaconfig.go b/lib/config/genconfig/infrahaconfig.go
new file mode 100644
index 000000000..cb91e84a5
--- /dev/null
+++ b/lib/config/genconfig/infrahaconfig.go
@@ -0,0 +1,21 @@
+package genconfig
+
+import (
+	"github.com/chef/automate/lib/toml"
+)
+
+type InfraHaConfig struct {
+	Fqdn string `json:"fqdn,omitempty" toml:"fqdn,omitempty" mapstructure:"fqdn,omitempty"`
+}
+
+func InfraHaConfigFactory() *InfraHaConfig {
+	return &InfraHaConfig{}
+}
+
+func (c *InfraHaConfig) Toml() (tomlBytes []byte, err error) {
+	return toml.Marshal(c)
+}
+
+func (c *InfraHaConfig) Prompts() (err error) {
+	return
+}
diff --git a/lib/config/genconfig/oshaconfig.go b/lib/config/genconfig/oshaconfig.go
new file mode 100644
index 000000000..2c3a4ec23
--- /dev/null
+++ b/lib/config/genconfig/oshaconfig.go
@@ -0,0 +1,21 @@
+package genconfig
+
+import (
+	"github.com/chef/automate/lib/toml"
+)
+
+type OsHaConfig struct {
+	Fqdn string `json:"fqdn,omitempty" toml:"fqdn,omitempty" mapstructure:"fqdn,omitempty"`
+}
+
+func OsHaConfigFactory() *OsHaConfig {
+	return &OsHaConfig{}
+}
+
+func (c *OsHaConfig) Toml() (tomlBytes []byte, err error) {
+	return toml.Marshal(c)
+}
+
+func (c *OsHaConfig) Prompts() (err error) {
+	return
+}
diff --git a/lib/config/genconfig/pghaconfig.go b/lib/config/genconfig/pghaconfig.go
new file mode 100644
index 000000000..2bbbd6859
--- /dev/null
+++ b/lib/config/genconfig/pghaconfig.go
@@ -0,0 +1,21 @@
+package genconfig
+
+import (
+	"github.com/chef/automate/lib/toml"
+)
+
+type PgHaConfig struct {
+	Fqdn string `json:"fqdn,omitempty" toml:"fqdn,omitempty" mapstructure:"fqdn,omitempty"`
+}
+
+func PgHaConfigFactory() *PgHaConfig {
+	return &PgHaConfig{}
+}
+
+func (c *PgHaConfig) Toml() (tomlBytes []byte, err error) {
+	return toml.Marshal(c)
+}
+
+func (c *PgHaConfig) Prompts() (err error) {
+	return
+}
diff --git a/lib/config/genconfig/standaloneconfig.go b/lib/config/genconfig/standaloneconfig.go
new file mode 100644
index 000000000..7f76daf8d
--- /dev/null
+++ b/lib/config/genconfig/standaloneconfig.go
@@ -0,0 +1,21 @@
+package genconfig
+
+import (
+	"github.com/chef/automate/lib/toml"
+)
+
+type AutomateStandaloneConfig struct {
+	Fqdn string `json:"fqdn,omitempty" toml:"fqdn,omitempty" mapstructure:"fqdn,omitempty"`
+}
+
+func AutomateStandaloneConfigFactory() *AutomateStandaloneConfig {
+	return &AutomateStandaloneConfig{}
+}
+
+func (c *AutomateStandaloneConfig) Toml() (tomlBytes []byte, err error) {
+	return toml.Marshal(c)
+}
+
+func (c *AutomateStandaloneConfig) Prompts() (err error) {
+	return
+}
diff --git a/lib/config/hadeployconfig.go b/lib/config/hadeployconfig.go
index 06ce1a302..b9b4f83df 100644
--- a/lib/config/hadeployconfig.go
+++ b/lib/config/hadeployconfig.go
@@ -239,3 +239,185 @@ func (c *HaDeployConfig) Parse(configFile string) error {
 	}
 	return nil
 }
+
+func (c *HaDeployConfig) InitArchitecture() *Architecture {
+	if c.Architecture == nil {
+		c.Architecture = &Architecture{}
+	}
+	return c.Architecture
+}
+
+func (c *Architecture) InitExistingInfra() *ConfigInitials {
+	if c.ExistingInfra == nil {
+		c.ExistingInfra = &ConfigInitials{}
+	}
+	return c.ExistingInfra
+}
+
+func (c *Architecture) InitAws() *ConfigInitials {
+	if c.Aws == nil {
+		c.Aws = &ConfigInitials{}
+	}
+	return c.Aws
+}
+
+func (c *HaDeployConfig) InitAutomate() *AutomateSettings {
+	if c.Automate == nil {
+		c.Automate = &AutomateSettings{}
+	}
+	return c.Automate
+}
+
+func (c *AutomateSettings) InitConfig() *ConfigAutomateSettings {
+	if c.Config == nil {
+		c.Config = &ConfigAutomateSettings{}
+	}
+	return c.Config
+}
+
+func (c *HaDeployConfig) InitChefServer() *ChefServerSettings {
+	if c.ChefServer == nil {
+		c.ChefServer = &ChefServerSettings{}
+	}
+	return c.ChefServer
+}
+
+func (c *ChefServerSettings) InitConfig() *ConfigChefServerSettings {
+	if c.Config == nil {
+		c.Config = &ConfigChefServerSettings{}
+	}
+	return c.Config
+}
+
+func (c *HaDeployConfig) InitOpenSearch() *OpensearchSettings {
+	if c.Opensearch == nil {
+		c.Opensearch = &OpensearchSettings{}
+	}
+	return c.Opensearch
+}
+
+func (c *OpensearchSettings) InitConfig() *ConfigOpensearchSettings {
+	if c.Config == nil {
+		c.Config = &ConfigOpensearchSettings{}
+	}
+	return c.Config
+}
+
+func (c *HaDeployConfig) InitPostgresql() *PostgresqlSettings {
+	if c.Postgresql == nil {
+		c.Postgresql = &PostgresqlSettings{}
+	}
+	return c.Postgresql
+}
+
+func (c *PostgresqlSettings) InitConfig() *ConfigSettings {
+	if c.Config == nil {
+		c.Config = &ConfigSettings{}
+	}
+	return c.Config
+}
+
+func (c *HaDeployConfig) InitExistingInfra() *ExistingInfraSettings {
+	if c.ExistingInfra == nil {
+		c.ExistingInfra = &ExistingInfraSettings{}
+	}
+	return c.ExistingInfra
+}
+
+func (c *ExistingInfraSettings) InitConfig() *ConfigExistingInfraSettings {
+	if c.Config == nil {
+		c.Config = &ConfigExistingInfraSettings{}
+	}
+	return c.Config
+}
+
+func (c *HaDeployConfig) InitExternal() *ExternalSettings {
+	if c.External == nil {
+		c.External = &ExternalSettings{}
+	}
+	return c.External
+}
+
+func (c *ExternalSettings) InitDatabase() *ExternalDBSettings {
+	if c.Database == nil {
+		c.Database = &ExternalDBSettings{}
+	}
+	return c.Database
+}
+
+func (c *ExternalDBSettings) InitPostgresql() *ExternalPgSettings {
+	if c.PostgreSQL == nil {
+		c.PostgreSQL = &ExternalPgSettings{}
+	}
+	return c.PostgreSQL
+}
+
+func (c *ExternalDBSettings) InitOpenSearch() *ExternalOsSettings {
+	if c.OpenSearch == nil {
+		c.OpenSearch = &ExternalOsSettings{}
+	}
+	return c.OpenSearch
+}
+
+func (c *ExternalOsSettings) InitOpenSearchAws() *AwsExternalOsSettings {
+	if c.Aws == nil {
+		c.Aws = &AwsExternalOsSettings{}
+	}
+	return c.Aws
+}
+
+func (c *ConfigAutomateSettings) InitCertsByIP() *[]CertByIP {
+	if c.CertsByIP == nil {
+		c.CertsByIP = &[]CertByIP{}
+	}
+	return c.CertsByIP
+}
+
+func (c *ConfigSettings) InitCertsByIP() *[]CertByIP {
+	if c.CertsByIP == nil {
+		c.CertsByIP = &[]CertByIP{}
+	}
+	return c.CertsByIP
+}
+
+func (c *ConfigChefServerSettings) InitCertsByIP() *[]CertByIP {
+	if c.CertsByIP == nil {
+		c.CertsByIP = &[]CertByIP{}
+	}
+	return c.CertsByIP
+}
+
+func (c *ConfigOpensearchSettings) InitCertsByIP() *[]CertByIP {
+	if c.CertsByIP == nil {
+		c.CertsByIP = &[]CertByIP{}
+	}
+	return c.CertsByIP
+}
+
+func (c *HaDeployConfig) InitAws() *AwsSettings {
+	if c.Aws == nil {
+		c.Aws = &AwsSettings{}
+	}
+	return c.Aws
+}
+
+func (c *AwsSettings) InitConfigAwsSettings() *ConfigAwsSettings {
+	if c.Config == nil {
+		c.Config = &ConfigAwsSettings{}
+	}
+	return c.Config
+}
+
+func (c *HaDeployConfig) InitObjectStorage() *ObjectStorage {
+	if c.ObjectStorage == nil {
+		c.ObjectStorage = &ObjectStorage{}
+	}
+	return c.ObjectStorage
+}
+
+func (c *ObjectStorage) InitConfig() *ConfigObjectStorage {
+	if c.Config == nil {
+		c.Config = &ConfigObjectStorage{}
+	}
+	return c.Config
+}
diff --git a/lib/hardwarecal/hardwarecal.go b/lib/hardwarecal/hardwarecal.go
new file mode 100644
index 000000000..7f2056be5
--- /dev/null
+++ b/lib/hardwarecal/hardwarecal.go
@@ -0,0 +1,238 @@
+package hardwarecal
+
+import (
+	"errors"
+	"fmt"
+	"math"
+)
+
+type HardwareCalReq struct {
+	NoOfNodes                     int
+	FreqCountComplianceScanPerDay int
+	FreqCountClientRunPerDay      int
+	FreqCountEventFeedPerDay      int
+
+	DataRetentionDays int
+
+	ComplianceReportSizeKB int
+	ClientRunReportSizeKB  int
+	EventFeedSizeKB        int
+
+	NoOfReplicasInOpenSearch int
+}
+
+type HardwareCalRes struct {
+	AutomateNode   *Node
+	ChefServerNode *Node
+	PostgresqlNode *Node
+	OpenSearchNode *Node
+}
+
+type Node struct {
+	InstanceCount int
+	CpuCount      int
+	RamGB         int
+	StorageGB     int
+	Type          string
+}
+
+func (h *HardwareCalReq) OpenSearchCal() (osNodeCount, opensearchRam, osNodeStorage int, err error) {
+	compReportsTotalRawSizePerDay := h.NoOfNodes * (h.FreqCountComplianceScanPerDay * h.ComplianceReportSizeKB)
+	clientReportsTotalRawSizePerDay := h.NoOfNodes * (h.FreqCountClientRunPerDay * h.ClientRunReportSizeKB)
+	eventReportsTotalRawSizePerDay := h.NoOfNodes * (h.FreqCountEventFeedPerDay * h.EventFeedSizeKB)
+
+	reportsTotalRawSizePerDay := compReportsTotalRawSizePerDay + clientReportsTotalRawSizePerDay + eventReportsTotalRawSizePerDay
+
+	retainedData := reportsTotalRawSizePerDay * h.DataRetentionDays
+	retainedDataGB := retainedData / 1024 / 1024
+
+	totalDataWithReplicationGB := retainedDataGB * (1 + h.NoOfReplicasInOpenSearch)
+
+	opensearchRam = 8
+	nodeCountFactor := float64(h.NoOfNodes) / 100000.0
+	if nodeCountFactor > 1 {
+		nodeCountFactor = 1
+	}
+	if nodeCountFactor < 0.8 {
+		nodeCountFactor = 0.8
+	}
+	MemoryDiskRatio := 160.0 / nodeCountFactor
+
+	osNodeCount = int(math.Ceil(float64(totalDataWithReplicationGB) / float64(opensearchRam) / MemoryDiskRatio))
+	if osNodeCount > 5 {
+		opensearchRam = 16
+		osNodeCount = int(math.Ceil(float64(totalDataWithReplicationGB) / float64(opensearchRam) / MemoryDiskRatio))
+	}
+	if osNodeCount > 5 {
+		opensearchRam = 32
+		osNodeCount = int(math.Ceil(float64(totalDataWithReplicationGB) / float64(opensearchRam) / MemoryDiskRatio))
+	}
+	if osNodeCount > 5 {
+		opensearchRam = 64
+		osNodeCount = int(math.Ceil(float64(totalDataWithReplicationGB) / float64(opensearchRam) / MemoryDiskRatio))
+	}
+
+	if osNodeCount%2 == 0 {
+		osNodeCount++
+	}
+	if osNodeCount < 3 {
+		osNodeCount = 3
+	}
+
+	IndexingOverhead := 0.1
+	DesiredShardSize := 50.0
+	ShardCount := int(float64(totalDataWithReplicationGB) * (1 + IndexingOverhead) / DesiredShardSize)
+
+	MaxShardCount := int(osNodeCount) * (opensearchRam / 2) * 20
+	if ShardCount > MaxShardCount {
+		return 0, 0, 0, errors.New(fmt.Sprint("MaxShardCount:", MaxShardCount, " is less than Shard Count: ", ShardCount))
+	}
+
+	if osNodeCount > 9 {
+		return 0, 0, 0, errors.New(fmt.Sprint("opensearch node count needed: ", osNodeCount, ", please see if you can reduce the input requirements so the OpenSearch Nodes needed comes down to max 9 for 64gb ram"))
+	}
+
+	diskWatermarkThreshold := 0.15
+	marginOfError := 0.1
+	totalStorageNeed := int(float64(totalDataWithReplicationGB) * (1 + diskWatermarkThreshold + marginOfError))
+	osNodeStorage = int(math.Ceil(float64(totalStorageNeed) / float64(osNodeCount)))
+	osNodeStorage = int(math.Round(float64(osNodeStorage)/100.0) * 100.0)
+
+	return
+}
+
+func (h *HardwareCalReq) PostgresqlCal() (pgNodeRam, pgStorage int) {
+	pgStorageNeedGB := (h.NoOfNodes * 20) / 1024
+	pgNodeRam = int(float64(pgStorageNeedGB) * 0.02)
+	if pgNodeRam < 8 {
+		pgNodeRam = 8
+	}
+	pgStorage = 200
+	if pgStorageNeedGB > pgStorage {
+		pgStorage = int(math.Round(float64(pgStorageNeedGB)/100.0) * 100.0)
+	}
+	return
+}
+
+func (h *HardwareCalReq) FrontendCal() (frontendNodeCount, frontendRam int) {
+	concurrentComp := h.NoOfNodes
+	if h.FreqCountComplianceScanPerDay == 0 {
+		concurrentComp = 0
+	}
+	concurrentClient := h.NoOfNodes
+	if h.FreqCountClientRunPerDay == 0 {
+		concurrentClient = 0
+	}
+	concurrentEvent := h.NoOfNodes
+	if h.FreqCountEventFeedPerDay == 0 {
+		concurrentEvent = 0
+	}
+	concurrencyNeeded := concurrentComp + concurrentClient + concurrentEvent
+
+	frontendNodeCount = 2
+	frontendRam = 8
+	processPerMB := 5
+	processesPerGB := processPerMB * 1024
+	_ = h.checkFrontendNodeCount(&frontendNodeCount, frontendRam, processesPerGB, concurrencyNeeded)
+
+	if frontendNodeCount > 3 {
+		frontendNodeCount = 2
+		frontendRam = 16
+		_ = h.checkFrontendNodeCount(&frontendNodeCount, frontendRam, processesPerGB, concurrencyNeeded)
+	}
+	if frontendNodeCount > 3 {
+		frontendNodeCount = 2
+		frontendRam = 32
+		_ = h.checkFrontendNodeCount(&frontendNodeCount, frontendRam, processesPerGB, concurrencyNeeded)
+	}
+	return
+}
+
+func (h *HardwareCalReq) checkFrontendNodeCount(frontendNodeCount *int, frontendRam, processesPerGB, concurrencyNeeded int) (maxConcurrency int) {
+	maxConcurrency = *frontendNodeCount * (frontendRam * processesPerGB)
+	for i := 0; i < 20; i++ {
+		if maxConcurrency < concurrencyNeeded {
+			*frontendNodeCount++
+			maxConcurrency = *frontendNodeCount * (frontendRam * processesPerGB)
+		}
+	}
+	return
+}
+
+func (h *HardwareCalReq) NewCal() (r *HardwareCalRes, err error) {
+	r = &HardwareCalRes{}
+
+	osNodeCount, opensearchRam, osNodeStorage, err := h.OpenSearchCal()
+	if err != nil {
+		return nil, err
+	}
+	osNode, err := GetAwsMtype(opensearchRam)
+	if err != nil {
+		return nil, err
+	}
+	osNode.InstanceCount = osNodeCount
+	osNode.StorageGB = osNodeStorage
+	r.OpenSearchNode = osNode
+
+	pgRam, pgStorage := h.PostgresqlCal()
+	r.PostgresqlNode, err = GetAwsMtype(pgRam)
+	if err != nil {
+		return nil, err
+	}
+	r.PostgresqlNode.InstanceCount = 3
+	r.PostgresqlNode.StorageGB = pgStorage
+
+	frontendNodeCount, frontendRam := h.FrontendCal()
+	r.AutomateNode, err = GetAwsMtype(frontendRam)
+	if err != nil {
+		return nil, err
+	}
+	r.AutomateNode.InstanceCount = frontendNodeCount
+	r.AutomateNode.StorageGB = 200
+
+	r.ChefServerNode, err = GetAwsMtype(frontendRam)
+	if err != nil {
+		return nil, err
+	}
+	r.ChefServerNode.InstanceCount = frontendNodeCount
+	r.ChefServerNode.StorageGB = 200
+
+	return
+}
+
+var AwsMtypes = []*Node{
+	&Node{
+		CpuCount: 2,
+		RamGB:    8,
+		Type:     "m5.large",
+	},
+	&Node{
+		CpuCount: 4,
+		RamGB:    16,
+		Type:     "m5.xlarge",
+	},
+	&Node{
+		CpuCount: 8,
+		RamGB:    32,
+		Type:     "m5.2xlarge",
+	},
+	&Node{
+		CpuCount: 16,
+		RamGB:    64,
+		Type:     "m5.4xlarge",
+	},
+}
+
+func GetAwsMtype(ramGB int) (*Node, error) {
+	for _, n := range AwsMtypes {
+		if ramGB <= n.RamGB {
+			newNode := &Node{
+				CpuCount: n.CpuCount,
+				RamGB:    n.RamGB,
+				Type:     n.Type,
+			}
+			return newNode, nil
+		}
+	}
+	return nil, errors.New("relevant aws machine type not found")
+}
diff --git a/lib/hardwarecal/hardwarecalprompt.go b/lib/hardwarecal/hardwarecalprompt.go
new file mode 100644
index 000000000..2334fe585
--- /dev/null
+++ b/lib/hardwarecal/hardwarecalprompt.go
@@ -0,0 +1,120 @@
+package hardwarecal
+
+import (
+	"errors"
+	"fmt"
+	"strconv"
+
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/chef/automate/lib/pmt"
+	"github.com/chef/automate/lib/reporting"
+	"github.com/jedib0t/go-pretty/v5/table"
+)
+
+type HardwareCalPrompt struct {
+	Prompt pmt.Prompt  `toml:"-"`
+	cw     *cli.Writer `toml:"-"`
+}
+
+func NewHardwareCalPrompt(p pmt.Prompt, cw *cli.Writer) *HardwareCalPrompt {
+	return &HardwareCalPrompt{
+		Prompt: p,
+		cw:     cw,
+	}
+}
+
+func getRangeValidationFunc(min, max int, appendErrMsg string) func(input string) error {
+	validate := func(input string) error {
+		val, err := strconv.Atoi(input)
+		if err != nil {
+			return errors.New("invalid int")
+		}
+		if val < min || val > max {
+			return fmt.Errorf(appendErrMsg, min, max)
+		}
+		return nil
+	}
+	return validate
+}
+
+const INPUT_ERR_MSG = "Calculator only supports in range of %v <= N <= %v. Please run load testing by yourself to determine infrastructure need"
+
+func (h *HardwareCalPrompt) Run() (err error) {
+	fmt.Println("*** This Hardware calculator has not been completely tested ***")
+	fmt.Println("*** Thus, we request you to do your own Benchmarking and then choose the hardware resource specifications ***")
+	req := &HardwareCalReq{}
+	req.NoOfNodes, err = h.Prompt.InputIntDefaultValidation("No. of nodes", 5000, getRangeValidationFunc(1000, 150000, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.FreqCountComplianceScanPerDay, err = h.Prompt.InputIntDefaultValidation("Compliance Scans per day for each node", 1, getRangeValidationFunc(0, 24, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.ComplianceReportSizeKB, err = h.Prompt.InputIntDefaultValidation("Compliance Scan avg. report size in KB", 1024, getRangeValidationFunc(100, 4*1024, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.FreqCountClientRunPerDay, err = h.Prompt.InputIntDefaultValidation("Client Runs per day for each node", 24, getRangeValidationFunc(0, 48, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.ClientRunReportSizeKB, err = h.Prompt.InputIntDefaultValidation("Client Run avg. report size in KB", 300, getRangeValidationFunc(10, 1024, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.FreqCountEventFeedPerDay, err = h.Prompt.InputIntDefaultValidation("Event Feed per day for each node", 24, getRangeValidationFunc(0, 48, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.EventFeedSizeKB, err = h.Prompt.InputIntDefaultValidation("Event Feed avg. report size in KB", 100, getRangeValidationFunc(1, 500, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.DataRetentionDays, err = h.Prompt.InputIntDefaultValidation("Data Retention days", 30, getRangeValidationFunc(1, 90, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	req.NoOfReplicasInOpenSearch, err = h.Prompt.InputIntDefaultValidation("No. of Replicas in OpenSearch", 1, getRangeValidationFunc(0, 3, INPUT_ERR_MSG))
+	if err != nil {
+		return
+	}
+
+	resp, err := req.NewCal()
+	if err != nil {
+		return errors.New(INPUT_ERR_MSG)
+	}
+
+	tbHeader := table.Row{"Instance", "Count", "vCPU", "RAM (GB)", "Storage Space (GB)", "AWS Machine Type"}
+
+	rows := []table.Row{
+		{"Chef Automate", resp.AutomateNode.InstanceCount, resp.AutomateNode.CpuCount, resp.AutomateNode.RamGB, resp.AutomateNode.StorageGB, resp.AutomateNode.Type},
+		{"Chef Infra Server", resp.ChefServerNode.InstanceCount, resp.ChefServerNode.CpuCount, resp.ChefServerNode.RamGB, resp.ChefServerNode.StorageGB, resp.ChefServerNode.Type},
+		{"PostgreSQL", resp.PostgresqlNode.InstanceCount, resp.PostgresqlNode.CpuCount, resp.PostgresqlNode.RamGB, resp.PostgresqlNode.StorageGB, resp.PostgresqlNode.Type},
+		{"OpenSearch", resp.OpenSearchNode.InstanceCount, resp.OpenSearchNode.CpuCount, resp.OpenSearchNode.RamGB, resp.OpenSearchNode.StorageGB, resp.OpenSearchNode.Type},
+	}
+
+	tb := &reporting.Table{
+		Title:     "Hardware Requirements",
+		Rows:      rows,
+		Header:    tbHeader,
+		Footer:    []interface{}{},
+		ColConfig: []table.ColumnConfig{},
+	}
+
+	tbMap := map[string]*reporting.Table{}
+	tbMap["HardwareRequirement"] = tb
+
+	report := reporting.NewReportingModule(h.cw, tbMap)
+	report.GenerateTableOutputAndPrint(tb)
+
+	return
+}
diff --git a/lib/io/fileutils/fileutils.go b/lib/io/fileutils/fileutils.go
index 1e4a7660e..163b2b4dd 100644
--- a/lib/io/fileutils/fileutils.go
+++ b/lib/io/fileutils/fileutils.go
@@ -4,7 +4,6 @@ import (
 	"bufio"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -38,6 +37,7 @@ type FileUtils interface {
 	Move(sourceFile string, destinationFile string) error
 	RemoveFirstLine(filePath string) error
 	GetFilePermission(filePath string) (int64, error)
+	Stat(name string) (os.FileInfo, error)
 }
 
 type FileSystemUtils struct{}
@@ -91,6 +91,10 @@ func (fsu *FileSystemUtils) GetFilePermission(filePath string) (int64, error) {
 	return GetFilePermission(filePath)
 }
 
+func (fsu *FileSystemUtils) Stat(name string) (os.FileInfo, error) {
+	return os.Stat(name)
+}
+
 // LogCLose closes the given io.Closer, logging any error.
 func LogClose(c io.Closer, log logrus.FieldLogger, msg string) {
 	if err := c.Close(); err != nil {
@@ -172,11 +176,19 @@ func WriteToFile(filepath string, data []byte) error {
 }
 
 func ReadFile(filename string) ([]byte, error) {
-	return ioutil.ReadFile(filename) // nosemgrep
+	home := os.Getenv("HOME")
+	path := filename
+	if strings.Index(path, "~") == 0 {
+		if home == "" {
+			return []byte{}, errors.New("path not found, as $HOME is not defined to resolve '~' in the given path: " + filename)
+		}
+		path = strings.Replace(path, "~", home, 1)
+	}
+	return os.ReadFile(path)
 }
 
 func WriteFile(filepath string, data []byte, perm os.FileMode) error {
-	return ioutil.WriteFile(filepath, data, perm) // nosemgrep
+	return os.WriteFile(filepath, data, perm)
 }
 
 func CreateTempFile(content string, filename string) (string, error) {
diff --git a/lib/io/fileutils/mockFileUtils.go b/lib/io/fileutils/mockFileUtils.go
index 1a512bc4c..f70516a27 100644
--- a/lib/io/fileutils/mockFileUtils.go
+++ b/lib/io/fileutils/mockFileUtils.go
@@ -1,7 +1,9 @@
 package fileutils
 
 import (
+	"io/fs"
 	"os"
+	"time"
 )
 
 type MockFileSystemUtils struct {
@@ -19,6 +21,7 @@ type MockFileSystemUtils struct {
 	MoveFunc                   func(sourceFile string, destinationFile string) error
 	RemoveFirstLineFunc        func(filePath string) error
 	GetFilePermissionFunc      func(filePath string) (int64, error)
+	StatFunc                   func(name string) (os.FileInfo, error)
 }
 
 func (fsu *MockFileSystemUtils) PathExists(path string) (bool, error) {
@@ -63,3 +66,45 @@ func (fsu *MockFileSystemUtils) RemoveFirstLine(filePath string) error {
 func (fsu *MockFileSystemUtils) GetFilePermission(filePath string) (int64, error) {
 	return fsu.GetFilePermissionFunc(filePath)
 }
+func (fsu *MockFileSystemUtils) Stat(name string) (os.FileInfo, error) {
+	return fsu.StatFunc(name)
+}
+
+type MockFileInfo struct {
+	NameFunc    func() string      // base name of the file
+	SizeFunc    func() int64       // length in bytes for regular files; system-dependent for others
+	ModeFunc    func() fs.FileMode // file mode bits
+	ModTimeFunc func() time.Time   // modification time
+	IsDirFunc   func() bool        // abbreviation for Mode().IsDir()
+	SysFunc     func() any         // underlying data source (can return nil)
+}
+
+// IsDir implements fs.FileInfo.
+func (mfi MockFileInfo) IsDir() bool {
+	return mfi.IsDirFunc()
+}
+
+// ModTime implements fs.FileInfo.
+func (mfi MockFileInfo) ModTime() time.Time {
+	return mfi.ModTimeFunc()
+}
+
+// Mode implements fs.FileInfo.
+func (mfi MockFileInfo) Mode() fs.FileMode {
+	return mfi.ModeFunc()
+}
+
+// Name implements fs.FileInfo.
+func (mfi MockFileInfo) Name() string {
+	return mfi.NameFunc()
+}
+
+// Size implements fs.FileInfo.
+func (mfi MockFileInfo) Size() int64 {
+	return mfi.SizeFunc()
+}
+
+// Sys implements fs.FileInfo.
+func (mfi MockFileInfo) Sys() any {
+	return mfi.SysFunc
+}
diff --git a/lib/ioutils/nopwritecloser.go b/lib/ioutils/nopwritecloser.go
new file mode 100644
index 000000000..8c4594047
--- /dev/null
+++ b/lib/ioutils/nopwritecloser.go
@@ -0,0 +1,14 @@
+package ioutils
+
+import "io"
+
+type NopWriteCloser struct {
+	io.Writer
+}
+
+func NewNopWriteCloser(w io.Writer) *NopWriteCloser {
+	return &NopWriteCloser{w}
+}
+
+func (wc *NopWriteCloser) Write(p []byte) (int, error) { return wc.Writer.Write(p) }
+func (wc *NopWriteCloser) Close() error                { return nil }
diff --git a/lib/pmt/prompt.go b/lib/pmt/prompt.go
new file mode 100644
index 000000000..f3a6c374a
--- /dev/null
+++ b/lib/pmt/prompt.go
@@ -0,0 +1,581 @@
+package pmt
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"regexp"
+	"regexp/syntax"
+	"strconv"
+	"strings"
+
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/manifoldco/promptui"
+)
+
+const (
+	FAIL_MSG         = "prompt failed %v"
+	INVALID_INT      = "invalid int"
+	PARSE_INT_FAIL   = "parse int failed %v"
+	REGEX_CHECK_FAIL = "regex check failed (%v) "
+)
+const esc = "\033["
+const moveDown = esc + "1B"
+const moveUp = esc + "1A"
+const selectKey = "\r"
+const clearLine = esc + "2K\n"
+
+type Prompt interface {
+	Confirm(question, trueOption, falseOption string) (result bool, err error)
+	Select(question string, options ...string) (index int, result string, err error)
+	SelectSearch(question string, options ...string) (index int, result string, err error)
+	SelectAdd(question string, addLabel string, options ...string) (index int, result string, err error)
+	InputInt64(label string) (resultVal int64, err error)
+	InputInt(label string) (resultVal int, err error)
+	InputIntDefault(label string, defVal int) (resultVal int, err error)
+	InputIntDefaultValidation(label string, defVal int, valFunc func(input string) error) (resultVal int, err error)
+	InputIntRange(label string, min, max int) (resultVal int, err error)
+	InputIntDefaultRange(label string, defVal, min, max int) (resultVal int, err error)
+	InputFloat64(label string) (resultVal float64, err error)
+	InputStringMinMax(label string, minlen int, maxlen int) (result string, err error)
+	InputWordDefault(label string, defaultVal string) (result string, err error)
+	InputWord(label string) (result string, err error)
+	InputStringRegex(label string, regexCheck string) (result string, err error)
+	InputString(label string) (result string, err error)
+	InputStringRequired(label string) (result string, err error)
+	InputPassword(label string) (result string, err error)
+	InputPasswordRegex(label string, regexCheck string) (result string, err error)
+	InputStringRegexDefault(label string, regexCheck string, defaultVal string) (result string, err error)
+	InputStringDefault(label string, defaultVal string) (result string, err error)
+	InputExistingFilePathDefault(label string, defaultVal string) (result string, err error)
+	InputExistingFilePath(label string) (result string, err error)
+}
+
+type PromptImp struct {
+	Stdin     io.ReadCloser
+	Stdout    io.WriteCloser
+	FileUtils fileutils.FileUtils
+}
+
+func PromptFactory(in io.ReadCloser, out io.WriteCloser, fu fileutils.FileUtils) *PromptImp {
+	return &PromptImp{
+		Stdin:     in,
+		Stdout:    out,
+		FileUtils: fu,
+	}
+}
+
+func (p *PromptImp) Confirm(question, trueOption, falseOption string) (resultVal bool, err error) {
+	prompt := promptui.Select{
+		Label:  question,
+		Items:  []string{trueOption, falseOption},
+		Stdin:  p.Stdin,
+		Stdout: p.Stdout,
+	}
+
+	_, result, err := prompt.Run()
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	switch result {
+	case trueOption:
+		return true, nil
+	case falseOption:
+		return false, nil
+	default:
+		return false, nil
+	}
+}
+
+func (p *PromptImp) SelectSearch(question string, options ...string) (index int, result string, err error) {
+	return p.selector(question, true, options...)
+}
+
+func (p *PromptImp) Select(question string, options ...string) (index int, result string, err error) {
+	return p.selector(question, false, options...)
+}
+
+func (p *PromptImp) selector(question string, searchMode bool, options ...string) (index int, result string, err error) {
+	searcher := func(input string, index int) bool {
+		item := options[index]
+		name := strings.Replace(strings.ToLower(item), " ", "", -1)
+		input = strings.Replace(strings.ToLower(input), " ", "", -1)
+
+		return strings.Contains(name, input)
+	}
+	prompt := promptui.Select{
+		Label:             question,
+		Items:             options,
+		Stdin:             p.Stdin,
+		Stdout:            p.Stdout,
+		Searcher:          searcher,
+		StartInSearchMode: searchMode,
+	}
+
+	return promptSelectRun(prompt, false)
+}
+
+func (p *PromptImp) SelectAdd(question string, addLabel string, options ...string) (index int, result string, err error) {
+	index = -1
+
+	for index < 0 {
+		prompt := promptui.SelectWithAdd{
+			Label:    question,
+			Items:    options,
+			AddLabel: addLabel,
+		}
+
+		index, result, err = prompt.Run()
+		if index == -1 {
+			options = append(options, result)
+		}
+	}
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputInt64(label string) (resultVal int64, err error) {
+	validate := func(input string) error {
+		_, err := strconv.ParseInt(input, 10, 64)
+		if err != nil {
+			return errors.New("invalid int64")
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	result, err := prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	resultVal, err = strconv.ParseInt(result, 10, 64)
+	if err != nil {
+		err = fmt.Errorf("parse int64 failed %v", err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputIntRange(label string, min, max int) (resultVal int, err error) {
+	validate := func(input string) error {
+		val, err := strconv.Atoi(input)
+		if err != nil {
+			return errors.New(INVALID_INT)
+		}
+		if val < min || val > max {
+			return fmt.Errorf("number should be in range: %v <= N <= %v", min, max)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	result, err := prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	resultVal, err = strconv.Atoi(result)
+	if err != nil {
+		err = fmt.Errorf(PARSE_INT_FAIL, err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputIntDefaultValidation(label string, defVal int, valFunc func(input string) error) (resultVal int, err error) {
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: valFunc,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+		Default:  fmt.Sprint(defVal),
+	}
+
+	result, err := prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	resultVal, err = strconv.Atoi(result)
+	if err != nil {
+		err = fmt.Errorf(PARSE_INT_FAIL, err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputIntDefaultRange(label string, defVal, min, max int) (resultVal int, err error) {
+	validate := func(input string) error {
+		val, err := strconv.Atoi(input)
+		if err != nil {
+			return errors.New(INVALID_INT)
+		}
+		if val < min || val > max {
+			return fmt.Errorf("number should be in range: %v <= N <= %v", min, max)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+		Default:  fmt.Sprint(defVal),
+	}
+
+	result, err := prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	resultVal, err = strconv.Atoi(result)
+	if err != nil {
+		err = fmt.Errorf(PARSE_INT_FAIL, err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputInt(label string) (resultVal int, err error) {
+	validate := func(input string) error {
+		_, err := strconv.Atoi(input)
+		if err != nil {
+			return errors.New(INVALID_INT)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	result, err := prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	resultVal, err = strconv.Atoi(result)
+	if err != nil {
+		err = fmt.Errorf(PARSE_INT_FAIL, err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputIntDefault(label string, defVal int) (resultVal int, err error) {
+	validate := func(input string) error {
+		_, err := strconv.Atoi(input)
+		if err != nil {
+			return errors.New(INVALID_INT)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+		Default:  fmt.Sprint(defVal),
+	}
+
+	result, err := prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	resultVal, err = strconv.Atoi(result)
+	if err != nil {
+		err = fmt.Errorf(PARSE_INT_FAIL, err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputFloat64(label string) (resultVal float64, err error) {
+	validate := func(input string) error {
+		_, err := strconv.ParseFloat(input, 64)
+		if err != nil {
+			return errors.New("invalid float64")
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	result, err := prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+
+	resultVal, err = strconv.ParseFloat(result, 64)
+	if err != nil {
+		err = fmt.Errorf("parse float64 failed %v", err)
+		return
+	}
+	return
+}
+
+func (p *PromptImp) InputWord(label string) (result string, err error) {
+	return p.InputWordDefault(label, "")
+}
+
+func (p *PromptImp) InputWordDefault(label string, defaultVal string) (result string, err error) {
+	validate := func(input string) error {
+		isWord := true
+		for _, v := range input {
+			isWord = syntax.IsWordChar(v)
+			if !isWord {
+				return fmt.Errorf("invalid word %v", err)
+			}
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Default:  defaultVal,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	return promptRun(prompt, true)
+}
+
+func (p *PromptImp) InputStringMinMax(label string, minlen int, maxlen int) (result string, err error) {
+	validate := func(input string) error {
+		trimInput := strings.TrimSpace(input)
+		if len(trimInput) < minlen {
+			return fmt.Errorf("smaller than %v", minlen)
+		} else if len(trimInput) > maxlen {
+			return fmt.Errorf("larger than %v", maxlen)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	return promptRun(prompt, true)
+}
+
+func (p *PromptImp) InputStringRegex(label string, regexCheck string) (result string, err error) {
+	validate := func(input string) error {
+		var check = regexp.MustCompile(regexCheck).MatchString
+		if !check(input) {
+			return fmt.Errorf(REGEX_CHECK_FAIL, regexCheck)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	return promptRun(prompt, true)
+}
+
+func (p *PromptImp) InputStringRegexDefault(label string, regexCheck string, defaultVal string) (result string, err error) {
+	validate := func(input string) error {
+		var check = regexp.MustCompile(regexCheck).MatchString
+		if !check(input) {
+			return fmt.Errorf(REGEX_CHECK_FAIL, regexCheck)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: validate,
+		Default:  defaultVal,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	return promptRun(prompt, true)
+}
+
+func (p *PromptImp) InputStringRequired(label string) (result string, err error) {
+	validate := func(input string) error {
+		trimInput := strings.TrimSpace(input)
+		if len(trimInput) < 1 {
+			return fmt.Errorf("this is a required field")
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+		Validate: validate,
+	}
+
+	return promptRun(prompt, true)
+}
+
+func promptRun(prompt promptui.Prompt, trim bool) (result string, err error) {
+	result, err = prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+	if trim {
+		result = strings.TrimSpace(result)
+	}
+	return
+}
+
+func promptSelectRun(prompt promptui.Select, trim bool) (index int, result string, err error) {
+	index, result, err = prompt.Run()
+
+	if err != nil {
+		err = fmt.Errorf(FAIL_MSG, err)
+		return
+	}
+	if trim {
+		result = strings.TrimSpace(result)
+	}
+	return
+}
+
+func (p *PromptImp) InputString(label string) (result string, err error) {
+	prompt := promptui.Prompt{
+		Label:  label,
+		Stdin:  p.Stdin,
+		Stdout: p.Stdout,
+	}
+
+	return promptRun(prompt, true)
+}
+func (p *PromptImp) InputPasswordRegex(label string, regexCheck string) (result string, err error) {
+	validate := func(input string) error {
+		var check = regexp.MustCompile(regexCheck).MatchString
+		if !check(input) {
+			return fmt.Errorf(REGEX_CHECK_FAIL, regexCheck)
+		}
+		return nil
+	}
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+		Mask:     '*',
+		Validate: validate,
+	}
+
+	return promptRun(prompt, false)
+}
+func (p *PromptImp) InputPassword(label string) (result string, err error) {
+	prompt := promptui.Prompt{
+		Label:  label,
+		Stdin:  p.Stdin,
+		Stdout: p.Stdout,
+		Mask:   '*',
+	}
+
+	return promptRun(prompt, false)
+}
+
+func (p *PromptImp) InputStringDefault(label string, defaultVal string) (result string, err error) {
+	prompt := promptui.Prompt{
+		Label:   label,
+		Default: defaultVal,
+		Stdin:   p.Stdin,
+		Stdout:  p.Stdout,
+	}
+
+	return promptRun(prompt, true)
+}
+
+func (p *PromptImp) InputExistingFilePathDefault(label string, defaultVal string) (result string, err error) {
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: p.validateFilePath,
+		Default:  defaultVal,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	return promptRun(prompt, false)
+}
+
+func (p *PromptImp) validateFilePath(input string) error {
+	home := os.Getenv("HOME")
+	path := input
+	if strings.Index(path, "~") == 0 {
+		path = strings.Replace(path, "~", home, 1)
+	}
+	f, err := p.FileUtils.Stat(path)
+	if err != nil {
+		return errors.New("file does not exist")
+	}
+	if f.IsDir() {
+		return errors.New("this is a directory")
+	}
+	return nil
+}
+
+func (p *PromptImp) InputExistingFilePath(label string) (result string, err error) {
+
+	prompt := promptui.Prompt{
+		Label:    label,
+		Validate: p.validateFilePath,
+		Stdin:    p.Stdin,
+		Stdout:   p.Stdout,
+	}
+
+	return promptRun(prompt, false)
+}
diff --git a/lib/sshutils/sshutils.go b/lib/sshutils/sshutils.go
index d975ed336..f3289addc 100644
--- a/lib/sshutils/sshutils.go
+++ b/lib/sshutils/sshutils.go
@@ -1,20 +1,28 @@
 package sshutils
 
 import (
+	"fmt"
 	"net"
 	"os"
 	"path/filepath"
 	"regexp"
 	"strings"
+	"sync"
 	"time"
 
+	"github.com/chef/automate/lib/io/fileutils"
 	"github.com/chef/automate/lib/logger"
 	"github.com/chef/automate/lib/platform/command"
+	"github.com/chef/automate/lib/stringutils"
 	"github.com/pkg/errors"
 	"golang.org/x/crypto/ssh"
 	"golang.org/x/crypto/ssh/knownhosts"
 )
 
+const AUTOMATE_KNOWN_HOSTS = ".automate_known_hosts"
+
+var mutex sync.Mutex
+
 type SSHConfig struct {
 	SshUser    string
 	SshPort    string
@@ -24,9 +32,10 @@ type SSHConfig struct {
 }
 
 type SSHUtilImpl struct {
-	SshClient ISshClient
-	logger    logger.Logger
-	Exec      command.Executor
+	SshClient  ISshClient
+	logger     logger.Logger
+	Exec       command.Executor
+	sshDirPath string
 }
 
 type SSHUtil interface {
@@ -108,6 +117,21 @@ func (s *SSHUtilImpl) GetClientConfig(sshConfig SSHConfig) (*ssh.ClientConfig, e
 		s.logger.Errorf("Parsing key failed: %v", err)
 		return nil, err
 	}
+	homePath := os.Getenv("HOME")
+	if homePath == "" {
+		return nil, errors.New("Environment variable HOME cannot be empty. Please set a value for HOME env")
+	}
+	sshDirPath := filepath.Join(os.Getenv("HOME"), ".ssh")
+	exists, err := fileutils.PathExists(sshDirPath)
+	if err != nil {
+		s.logger.Errorln("Error evaluating path:", err)
+		return nil, err
+	}
+	if !exists {
+		s.logger.Errorf("Path %s does not exist\n", sshDirPath)
+		return nil, errors.New(fmt.Sprintf("Path %s does not exist", sshDirPath))
+	}
+	s.sshDirPath = sshDirPath
 	// Client config
 	return &ssh.ClientConfig{
 		User:            sshConfig.SshUser,
@@ -117,8 +141,11 @@ func (s *SSHUtilImpl) GetClientConfig(sshConfig SSHConfig) (*ssh.ClientConfig, e
 }
 
 func (s *SSHUtilImpl) HostKeyCallback(host string, remote net.Addr, pubkey ssh.PublicKey) error {
+	mutex.Lock()
+	defer mutex.Unlock()
+	knownHostPath := filepath.Join(s.sshDirPath, AUTOMATE_KNOWN_HOSTS)
 	var keyErr *knownhosts.KeyError
-	kh, err := s.CheckKnownHosts()
+	kh, err := s.CheckKnownHosts(knownHostPath)
 	if err != nil {
 		s.logger.Error("Error while getting the list of knows Host:", err)
 		return err
@@ -134,7 +161,7 @@ func (s *SSHUtilImpl) HostKeyCallback(host string, remote net.Addr, pubkey ssh.P
 		return keyErr
 	} else if errors.As(hErr, &keyErr) && len(keyErr.Want) == 0 {
 		// host key not found in known_hosts then give a warning and continue to connect.
-		return s.AddHostKey(host, remote, pubkey)
+		return s.AddHostKey(host, knownHostPath, remote, pubkey)
 	}
 	return nil
 }
@@ -296,8 +323,8 @@ func (s *SSHUtilImpl) CopyFileToRemoteConcurrently(sshConfig SSHConfig, srcFileP
 	return results
 }
 
-func (s *SSHUtilImpl) CreateKnownHosts() error {
-	f, fErr := os.OpenFile(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"), os.O_CREATE, 0600)
+func (s *SSHUtilImpl) CreateKnownHosts(knownHostPath string) error {
+	f, fErr := os.OpenFile(knownHostPath, os.O_CREATE, 0600)
 	if fErr != nil {
 		return fErr
 	}
@@ -305,12 +332,12 @@ func (s *SSHUtilImpl) CreateKnownHosts() error {
 	return nil
 }
 
-func (s *SSHUtilImpl) CheckKnownHosts() (ssh.HostKeyCallback, error) {
-	err := s.CreateKnownHosts()
+func (s *SSHUtilImpl) CheckKnownHosts(knownHostPath string) (ssh.HostKeyCallback, error) {
+	err := s.CreateKnownHosts(knownHostPath)
 	if err != nil {
 		return nil, err
 	}
-	kh, e := s.SshClient.New(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
+	kh, e := s.SshClient.New(knownHostPath)
 	if e != nil {
 		s.logger.Error("Error while getting the known host: %v", e)
 		return nil, e
@@ -318,19 +345,35 @@ func (s *SSHUtilImpl) CheckKnownHosts() (ssh.HostKeyCallback, error) {
 	return kh, nil
 }
 
-func (s *SSHUtilImpl) AddHostKey(host string, remote net.Addr, pubKey ssh.PublicKey) error {
-	// add host key if host is not found in known_hosts, error object is return, if nil then connection proceeds,
+func (s *SSHUtilImpl) AddHostKey(host, known_hosts_path string, remote net.Addr, pubKey ssh.PublicKey) error {
+	// add host key if host is not found in known_hosts, error object is returned, if nil then connection proceeds,
 	// if not nil then connection stops.
-	khFilePath := filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts")
+	content, err := os.ReadFile(known_hosts_path)
+	if err != nil {
+		s.logger.Error("Error while reading the known hosts file:", err)
+		return err
+	}
+	lastLine := stringutils.GetLastLine(string(content))
 
-	f, fErr := os.OpenFile(khFilePath, os.O_APPEND|os.O_WRONLY, 0600)
-	if fErr != nil {
-		s.logger.Error("Error while opening file on the given path:", fErr)
-		return fErr
+	// Open the known hosts file in append mode
+	f, err := os.OpenFile(known_hosts_path, os.O_APPEND|os.O_WRONLY, 0600)
+	if err != nil {
+		s.logger.Error("Error while opening file on the given path:", err)
+		return err
 	}
 	defer f.Close()
+
+	// Write the host key entry to the known hosts file
 	knownHosts := s.SshClient.Normalize(remote.String())
-	_, fileErr := f.WriteString(knownhosts.Line([]string{knownHosts}, pubKey))
-	f.WriteString("\n")
-	return fileErr
+	line := knownhosts.Line([]string{knownHosts}, pubKey)
+	if lastLine != "" {
+		line = "\n" + line
+	}
+	_, err = f.WriteString(line)
+	if err != nil {
+		s.logger.Error("Error while writing to the known hosts file:", err)
+		return err
+	}
+
+	return nil
 }
diff --git a/lib/stringutils/strings.go b/lib/stringutils/strings.go
index 4a19ce794..07827d6a0 100644
--- a/lib/stringutils/strings.go
+++ b/lib/stringutils/strings.go
@@ -28,3 +28,9 @@ func TitleReplace(s string, sep string, rep string) string {
 func IsNumeric(word string) bool {
 	return regexp.MustCompile(`^[0-9]+$`).MatchString(word)
 }
+
+func GetLastLine(input string) string {
+	lines := strings.Split(input, "\n")
+	lastLine := lines[len(lines)-1]
+	return lastLine
+}
diff --git a/lib/toml/toml.go b/lib/toml/toml.go
new file mode 100644
index 000000000..6e4694b41
--- /dev/null
+++ b/lib/toml/toml.go
@@ -0,0 +1,60 @@
+package toml
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+
+	"github.com/pkg/errors"
+
+	toml "github.com/chef/toml"
+)
+
+// Unmarshal parses the TOML encoded data and stores the result in the
+// value pointed to by v.
+func Unmarshal(data []byte, v interface{}) error {
+	return toml.Unmarshal(data, v)
+}
+
+// StrictUnmarshal parses the TOML encoded data and stores the result
+// in the value pointed to by v. Unlike Unmarshal, any unknown keys in
+// the data is an error.
+func StrictUnmarshal(data []byte, v interface{}) error {
+	md, err := toml.Decode(string(data), v)
+	if err != nil {
+		return err
+	}
+
+	undecoded := md.Undecoded()
+	if len(undecoded) > 0 {
+		msg := strings.Builder{}
+		if len(undecoded) == 1 {
+			msg.WriteString(fmt.Sprintf("unknown configuration key %q", undecoded[0]))
+		} else {
+			msg.WriteString("unknown configuration keys:")
+			for _, v := range undecoded {
+				msg.WriteString("\n    ")
+				msg.WriteString(v.String())
+			}
+		}
+		return errors.New(msg.String())
+	}
+
+	return nil
+}
+
+// Marshal returns the TOML encoding of v.
+func Marshal(v interface{}) ([]byte, error) {
+	buf := new(bytes.Buffer)
+	enc := toml.NewEncoder(buf)
+	err := enc.Encode(v)
+	return buf.Bytes(), err
+}
+
+// Validate returns nil if data is valid TOML. Otherwise, an error
+// describing the parse failure is returned.
+func Validate(data []byte) error {
+	var tmp interface{}
+	err := toml.Unmarshal(data, &tmp)
+	return err
+}


Test patch:
diff --git a/lib/config/genconfig/awshaprovisionconfig_test.go b/lib/config/genconfig/awshaprovisionconfig_test.go
new file mode 100644
index 000000000..14c3e24fc
--- /dev/null
+++ b/lib/config/genconfig/awshaprovisionconfig_test.go
@@ -0,0 +1,1555 @@
+package genconfig
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"testing"
+
+	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/ioutils"
+	"github.com/chef/automate/lib/pmt"
+	"github.com/stretchr/testify/assert"
+)
+
+func pad(siz int, buf *bytes.Buffer) {
+	pu := make([]byte, 4096-siz)
+	for i := 0; i < 4096-siz; i++ {
+		pu[i] = 97
+	}
+	buf.Write(pu)
+}
+
+func input(buf *bytes.Buffer, input string) {
+	buf.WriteString(input)
+	pad(len(input), buf)
+}
+
+func TestPromptsCidrAWSManaged(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	sshUser := "ec2-user"
+	input(b, sshUser+"\r")
+
+	sshGroup := "ec2-user"
+	input(b, "\r")
+
+	sshPort := 22
+	input(b, "\r")
+
+	sshKeyFilePath := "test.pem"
+	sshKeyFileContent := "ssh user private key"
+	input(b, sshKeyFilePath+"\r")
+
+	enableCustomCerts := false
+	input(b, "\r")
+
+	awsProfile := "default"
+	input(b, "\r")
+
+	awsRegion := "ap-south-2"
+	input(b, "asia"+moveDown+"\r")
+
+	vpcId := "vpc-123"
+	input(b, vpcId+"\r")
+
+	useCidr := true
+	input(b, "\r")
+
+	cidr := "172.168.192.0"
+	input(b, cidr+"\r")
+
+	awsSshKeyPairName := "my-key-pair"
+	input(b, awsSshKeyPairName+"\r")
+
+	awsAmiId := "ami-123"
+	input(b, awsAmiId+"\r")
+
+	delOnTerm := false
+	input(b, "\r")
+
+	enableAccessLogsLb := true
+	input(b, "\r")
+
+	automateFqdn := "A2-automate.amazonaws.com"
+	input(b, automateFqdn+"\r")
+
+	automateLbCertArn := "arn-123"
+	input(b, automateLbCertArn+"\r")
+
+	automateFqdnRootCaFilePath := "automateFqdnRootCa.pem"
+	automateFqdnRootCaFileContent := "Automate Root CA"
+	input(b, automateFqdnRootCaFilePath+"\r")
+
+	automateAdminPass := "admin12345"
+	input(b, automateAdminPass+"\r")
+
+	automateNodeCount := 1
+	input(b, fmt.Sprint(automateNodeCount)+"\r")
+
+	automateInstanceType := "t3.medium"
+	input(b, "\r")
+
+	automateVolSize := 200
+	input(b, "\r")
+
+	automateVolType := "gp3"
+	input(b, "\r")
+
+	automateVolIops := 100
+	input(b, "\r")
+
+	chefInfraServerFqdn := "chef-infra-server.amazonaws.com"
+	input(b, chefInfraServerFqdn+"\r")
+
+	chefInfraServerLbCertArn := "arn-124"
+	input(b, chefInfraServerLbCertArn+"\r")
+
+	chefInfraServerFqdnRootCaFilePath := "chefInfraServerFqdnRootCa.pem"
+	chefInfraServerFqdnRootCaFileContent := "Chef Infra Server Root CA"
+	input(b, chefInfraServerFqdnRootCaFilePath+"\r")
+
+	chefInfraServerNodeCount := 1
+	input(b, fmt.Sprint(chefInfraServerNodeCount)+"\r")
+
+	chefInfraServerInstanceType := "t3.medium"
+	input(b, "\r")
+
+	chefInfraServerVolSize := 200
+	input(b, "\r")
+
+	chefInfraServerVolType := "gp3"
+	input(b, "\r")
+
+	chefInfraServerVolIops := 100
+	input(b, "\r")
+
+	useAwsManagedServices := true
+	input(b, "\r")
+
+	osDomainName := "opensearch-automate"
+	input(b, osDomainName+"\r")
+
+	osDomainUrl := "opensearch.automate.com"
+	input(b, osDomainUrl+"\r")
+
+	osUser := "admin"
+	input(b, osUser+"\r")
+
+	osPass := "admin@1234"
+	input(b, osPass+"\r")
+
+	osDefaultCerts := true
+	input(b, "\r")
+
+	osSnapshotRoleArn := "arn-os-snapshot-role"
+	input(b, osSnapshotRoleArn+"\r")
+
+	osSnapshotUserAccessKeyId := "ASKEIDSKFLDKDFJERKDS"
+	input(b, osSnapshotUserAccessKeyId+"\r")
+
+	osSnapshotUserAccessKeySecret := "asdfelkjsldfklsdjflsdfjdklsfjsdfsldkfjsd"
+	input(b, osSnapshotUserAccessKeySecret+"\r")
+
+	pgUrl := "pg.automate.com:5432"
+	input(b, pgUrl+"\r")
+
+	pgSuperUserName := "root"
+	input(b, pgSuperUserName+"\r")
+
+	pgSuperUserPass := "root1234"
+	input(b, pgSuperUserPass+"\r")
+
+	pgDbUserName := "dbUser"
+	input(b, pgDbUserName+"\r")
+
+	pgDbUserPass := "dbUser1234"
+	input(b, pgDbUserPass+"\r")
+
+	pgDefaultCerts := true
+	input(b, "\r")
+
+	enableBackup := true
+	input(b, "\r")
+
+	awsBackupBucketName := "automate-backup"
+	input(b, awsBackupBucketName+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == sshKeyFilePath {
+				data = []byte(sshKeyFileContent)
+			} else if filepath == automateFqdnRootCaFilePath {
+				data = []byte(automateFqdnRootCaFileContent)
+			} else if filepath == chefInfraServerFqdnRootCaFilePath {
+				data = []byte(chefInfraServerFqdnRootCaFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == sshKeyFilePath ||
+				name == automateFqdnRootCaFilePath ||
+				name == chefInfraServerFqdnRootCaFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.NoError(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "aws"
+	workspacePath := "/hab/a2_deploy_workspace"
+	automateConfigFile := "configs/automate.toml"
+	pgInstanceCount := 0
+	osInstanceCount := 0
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.Aws.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.Aws.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.Aws.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.Aws.WorkspacePath)
+
+	assert.Equal(t, sshUser, c.Config.Architecture.Aws.SSHUser)
+	assert.Equal(t, sshGroup, c.Config.Architecture.Aws.SSHGroupName)
+	assert.Equal(t, fmt.Sprint(sshPort), c.Config.Architecture.Aws.SSHPort)
+	assert.Equal(t, sshKeyFilePath, c.Config.Architecture.Aws.SSHKeyFile)
+
+	assert.Equal(t, enableCustomCerts, c.HasCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.Automate.Config.EnableCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.ChefServer.Config.EnableCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.Postgresql.Config.EnableCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.Opensearch.Config.EnableCustomCerts)
+
+	assert.Equal(t, awsProfile, c.Config.Aws.Config.Profile)
+	assert.Equal(t, awsRegion, c.Config.Aws.Config.Region)
+	assert.Equal(t, vpcId, c.Config.Aws.Config.AwsVpcID)
+	assert.Equal(t, useCidr, len(c.Config.Aws.Config.AwsCidrBlockAddr) > 0)
+	assert.Equal(t, cidr, c.Config.Aws.Config.AwsCidrBlockAddr)
+
+	assert.Equal(t, awsSshKeyPairName, c.Config.Aws.Config.SSHKeyPairName)
+	assert.Equal(t, awsAmiId, c.Config.Aws.Config.AmiID)
+	assert.Equal(t, delOnTerm, c.Config.Aws.Config.DeleteOnTermination)
+	assert.Equal(t, fmt.Sprint(enableAccessLogsLb), c.Config.Aws.Config.LbAccessLogs)
+
+	assert.Equal(t, automateFqdn, c.Config.Automate.Config.Fqdn)
+	assert.Equal(t, automateLbCertArn, c.Config.Aws.Config.AutomateLbCertificateArn)
+	assert.Equal(t, automateFqdnRootCaFileContent, c.Config.Automate.Config.FqdnRootCA)
+	assert.Equal(t, automateConfigFile, c.Config.Automate.Config.ConfigFile)
+	assert.Equal(t, automateAdminPass, c.Config.Automate.Config.AdminPassword)
+	assert.Equal(t, fmt.Sprint(automateNodeCount), c.Config.Automate.Config.InstanceCount)
+	assert.Equal(t, automateInstanceType, c.Config.Aws.Config.AutomateServerInstanceType)
+	assert.Equal(t, fmt.Sprint(automateVolSize), c.Config.Aws.Config.AutomateEbsVolumeSize)
+	assert.Equal(t, automateVolType, c.Config.Aws.Config.AutomateEbsVolumeType)
+	assert.Equal(t, fmt.Sprint(automateVolIops), c.Config.Aws.Config.AutomateEbsVolumeIops)
+
+	assert.Equal(t, chefInfraServerFqdn, c.Config.ChefServer.Config.ChefServerFqdn)
+	assert.Equal(t, chefInfraServerLbCertArn, c.Config.Aws.Config.ChefServerLbCertificateArn)
+	assert.Equal(t, chefInfraServerFqdnRootCaFileContent, c.Config.ChefServer.Config.FqdnRootCA)
+	assert.Equal(t, fmt.Sprint(chefInfraServerNodeCount), c.Config.ChefServer.Config.InstanceCount)
+	assert.Equal(t, chefInfraServerInstanceType, c.Config.Aws.Config.ChefServerInstanceType)
+	assert.Equal(t, fmt.Sprint(chefInfraServerVolSize), c.Config.Aws.Config.ChefEbsVolumeSize)
+	assert.Equal(t, chefInfraServerVolType, c.Config.Aws.Config.ChefEbsVolumeType)
+	assert.Equal(t, fmt.Sprint(chefInfraServerVolIops), c.Config.Aws.Config.ChefEbsVolumeIops)
+
+	assert.Equal(t, useAwsManagedServices, c.Config.Aws.Config.SetupManagedServices)
+
+	assert.Equal(t, osDomainName, c.Config.Aws.Config.ManagedOpensearchDomainName)
+	assert.Equal(t, osDomainUrl, c.Config.Aws.Config.ManagedOpensearchDomainURL)
+	assert.Equal(t, osUser, c.Config.Aws.Config.ManagedOpensearchUsername)
+	assert.Equal(t, osPass, c.Config.Aws.Config.ManagedOpensearchUserPassword)
+	assert.Equal(t, osDefaultCerts, len(c.Config.Aws.Config.ManagedOpensearchCertificate) == 0)
+	assert.Equal(t, osSnapshotRoleArn, c.Config.Aws.Config.AwsOsSnapshotRoleArn)
+	assert.Equal(t, osSnapshotUserAccessKeyId, c.Config.Aws.Config.OsSnapshotUserAccessKeyID)
+	assert.Equal(t, osSnapshotUserAccessKeySecret, c.Config.Aws.Config.OsSnapshotUserAccessKeySecret)
+
+	assert.Equal(t, pgUrl, c.Config.Aws.Config.ManagedRdsInstanceURL)
+	assert.Equal(t, pgSuperUserName, c.Config.Aws.Config.ManagedRdsSuperuserUsername)
+	assert.Equal(t, pgSuperUserPass, c.Config.Aws.Config.ManagedRdsSuperuserPassword)
+	assert.Equal(t, pgDbUserName, c.Config.Aws.Config.ManagedRdsDbuserUsername)
+	assert.Equal(t, pgDbUserPass, c.Config.Aws.Config.ManagedRdsDbuserPassword)
+	assert.Equal(t, pgDefaultCerts, len(c.Config.Aws.Config.ManagedRdsCertificate) == 0)
+
+	assert.Equal(t, fmt.Sprint(osInstanceCount), c.Config.Opensearch.Config.InstanceCount)
+	assert.Equal(t, fmt.Sprint(pgInstanceCount), c.Config.Postgresql.Config.InstanceCount)
+
+	assert.Equal(t, enableBackup, len(c.Config.Architecture.Aws.BackupConfig) > 0)
+	assert.Equal(t, "s3", c.Config.Architecture.Aws.BackupConfig)
+	assert.Equal(t, awsBackupBucketName, c.Config.Architecture.Aws.S3BucketName)
+}
+
+func TestPromptBackup(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	enableBackup := true
+	input(b, "\r")
+
+	backupConfig := "efs"
+	input(b, moveDown+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptBackup()
+
+	assert.NoError(t, err)
+	assert.Equal(t, enableBackup, len(c.Config.Architecture.Aws.BackupConfig) > 0)
+	assert.Equal(t, "/mnt/automate_backups", c.Config.Architecture.Aws.BackupMount)
+	assert.Equal(t, backupConfig, c.Config.Architecture.Aws.BackupConfig)
+}
+
+func TestPromptDatabases(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	isAwsManagedDb := false
+	input(b, moveDown+"\r")
+
+	pgInstanceCount := 3
+	input(b, fmt.Sprint(pgInstanceCount)+"\r")
+
+	pgInstanceType := "m5.large"
+	input(b, "\r")
+
+	pgVolSize := 200
+	input(b, "\r")
+
+	pgVolType := "gp3"
+	input(b, "\r")
+
+	pgVolIops := 100
+	input(b, "\r")
+
+	osInstanceCount := 3
+	input(b, fmt.Sprint(osInstanceCount)+"\r")
+
+	osInstanceType := "m5.large"
+	input(b, "\r")
+
+	osVolSize := 200
+	input(b, "\r")
+
+	osVolType := "gp3"
+	input(b, "\r")
+
+	osVolIops := 100
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptDatabases()
+
+	assert.NoError(t, err)
+	assert.Equal(t, isAwsManagedDb, c.Config.Aws.Config.SetupManagedServices)
+	assert.Equal(t, fmt.Sprint(pgInstanceCount), c.Config.Postgresql.Config.InstanceCount)
+	assert.Equal(t, pgInstanceType, c.Config.Aws.Config.PostgresqlServerInstanceType)
+	assert.Equal(t, fmt.Sprint(pgVolSize), c.Config.Aws.Config.PostgresqlEbsVolumeSize)
+	assert.Equal(t, pgVolType, c.Config.Aws.Config.PostgresqlEbsVolumeType)
+	assert.Equal(t, fmt.Sprint(pgVolIops), c.Config.Aws.Config.PostgresqlEbsVolumeIops)
+	assert.Equal(t, fmt.Sprint(osInstanceCount), c.Config.Opensearch.Config.InstanceCount)
+	assert.Equal(t, osInstanceType, c.Config.Aws.Config.OpensearchServerInstanceType)
+	assert.Equal(t, fmt.Sprint(osVolSize), c.Config.Aws.Config.OpensearchEbsVolumeSize)
+	assert.Equal(t, osVolType, c.Config.Aws.Config.OpensearchEbsVolumeType)
+	assert.Equal(t, fmt.Sprint(osVolIops), c.Config.Aws.Config.OpensearchEbsVolumeIops)
+}
+
+func TestPromptCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	certFilePath := "test.pem"
+	certFileContent := "ROOT CA"
+	input(b, certFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == certFilePath {
+				data = []byte(certFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	certVal, err := c.PromptCert("", OPENSEARCH_NODETYPE, ROOTCA)
+
+	assert.NoError(t, err)
+	assert.Equal(t, certFileContent, certVal)
+}
+
+func TestPromptAutomateNodes(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	automateInstanceCount := 1
+	input(b, fmt.Sprint(automateInstanceCount)+"\r")
+
+	enableCustomCert := true
+	input(b, "\r")
+
+	priKeyFilePath := "test.key"
+	priKeyFileContent := "PRIVATE KEY"
+	input(b, priKeyFilePath+"\r")
+
+	pubKeyFilePath := "test.pem"
+	pubKeyFileContent := "PUBLIC KEY"
+	input(b, pubKeyFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath {
+				data = []byte(priKeyFileContent)
+			} else if filepath == pubKeyFilePath {
+				data = []byte(pubKeyFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath ||
+				name == pubKeyFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCert,
+	}
+
+	err := c.PromptAutomateNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(automateInstanceCount), c.Config.Automate.Config.InstanceCount)
+	assert.Equal(t, enableCustomCert, c.HasCustomCerts)
+	assert.Equal(t, enableCustomCert, c.Config.Automate.Config.EnableCustomCerts)
+	assert.Equal(t, priKeyFileContent, c.Config.Automate.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent, c.Config.Automate.Config.PublicKey)
+}
+
+func TestPromptChefInfraServerNodes(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	chefInfraServerInstanceCount := 1
+	input(b, fmt.Sprint(chefInfraServerInstanceCount)+"\r")
+
+	enableCustomCert := true
+	input(b, "\r")
+
+	priKeyFilePath := "test.key"
+	priKeyFileContent := "PRIVATE KEY"
+	input(b, priKeyFilePath+"\r")
+
+	pubKeyFilePath := "test.pem"
+	pubKeyFileContent := "PUBLIC KEY"
+	input(b, pubKeyFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath {
+				data = []byte(priKeyFileContent)
+			} else if filepath == pubKeyFilePath {
+				data = []byte(pubKeyFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath ||
+				name == pubKeyFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCert,
+	}
+
+	err := c.PromptChefInfraServerNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(chefInfraServerInstanceCount), c.Config.ChefServer.Config.InstanceCount)
+	assert.Equal(t, enableCustomCert, c.HasCustomCerts)
+	assert.Equal(t, enableCustomCert, c.Config.ChefServer.Config.EnableCustomCerts)
+	assert.Equal(t, priKeyFileContent, c.Config.ChefServer.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent, c.Config.ChefServer.Config.PublicKey)
+}
+
+func TestPromptPostgresqlNodes(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	pgInstanceCount := 3
+	input(b, fmt.Sprint(pgInstanceCount)+"\r")
+
+	enableCustomCert := true
+	input(b, "\r")
+
+	priKeyFilePath := "test.key"
+	priKeyFileContent := "PRIVATE KEY"
+	input(b, priKeyFilePath+"\r")
+
+	pubKeyFilePath := "test.pem"
+	pubKeyFileContent := "PUBLIC KEY"
+	input(b, pubKeyFilePath+"\r")
+
+	rootCaFilePath := "rootca.pem"
+	rootCaFileContent := "ROOT CA"
+	input(b, rootCaFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath {
+				data = []byte(priKeyFileContent)
+			} else if filepath == pubKeyFilePath {
+				data = []byte(pubKeyFileContent)
+			} else if filepath == rootCaFilePath {
+				data = []byte(rootCaFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath ||
+				name == pubKeyFilePath ||
+				name == rootCaFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCert,
+	}
+
+	err := c.PromptPostgresqlNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(pgInstanceCount), c.Config.Postgresql.Config.InstanceCount)
+	assert.Equal(t, enableCustomCert, c.HasCustomCerts)
+	assert.Equal(t, enableCustomCert, c.Config.Postgresql.Config.EnableCustomCerts)
+	assert.Equal(t, priKeyFileContent, c.Config.Postgresql.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent, c.Config.Postgresql.Config.PublicKey)
+	assert.Equal(t, rootCaFileContent, c.Config.Postgresql.Config.RootCA)
+}
+
+func TestPromptOpenSearchNodes(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	osInstanceCount := 3
+	input(b, fmt.Sprint(osInstanceCount)+"\r")
+
+	enableCustomCert := true
+	input(b, "\r")
+
+	priKeyFilePath := "test.key"
+	priKeyFileContent := "PRIVATE KEY"
+	input(b, priKeyFilePath+"\r")
+
+	pubKeyFilePath := "test.pem"
+	pubKeyFileContent := "PUBLIC KEY"
+	input(b, pubKeyFilePath+"\r")
+
+	rootCaFilePath := "rootca.pem"
+	rootCaFileContent := "ROOT CA"
+	input(b, rootCaFilePath+"\r")
+
+	adminCertFilePath := "admin.pem"
+	adminCertFileContent := "ADMIN CERT"
+	input(b, adminCertFilePath+"\r")
+
+	adminKeyFilePath := "admin.key"
+	adminKeyFileContent := "ADMIN KEY"
+	input(b, adminKeyFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath {
+				data = []byte(priKeyFileContent)
+			} else if filepath == pubKeyFilePath {
+				data = []byte(pubKeyFileContent)
+			} else if filepath == rootCaFilePath {
+				data = []byte(rootCaFileContent)
+			} else if filepath == adminCertFilePath {
+				data = []byte(adminCertFileContent)
+			} else if filepath == adminKeyFilePath {
+				data = []byte(adminKeyFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath ||
+				name == pubKeyFilePath ||
+				name == rootCaFilePath ||
+				name == adminCertFilePath ||
+				name == adminKeyFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCert,
+	}
+
+	err := c.PromptOpenSearchNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(osInstanceCount), c.Config.Opensearch.Config.InstanceCount)
+	assert.Equal(t, enableCustomCert, c.HasCustomCerts)
+	assert.Equal(t, enableCustomCert, c.Config.Opensearch.Config.EnableCustomCerts)
+	assert.Equal(t, priKeyFileContent, c.Config.Opensearch.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent, c.Config.Opensearch.Config.PublicKey)
+	assert.Equal(t, rootCaFileContent, c.Config.Opensearch.Config.RootCA)
+	assert.Equal(t, adminCertFileContent, c.Config.Opensearch.Config.AdminCert)
+	assert.Equal(t, adminKeyFileContent, c.Config.Opensearch.Config.AdminKey)
+}
+
+func TestPromptPrivateSubnet(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	subnet1 := "subnet-1"
+	input(b, subnet1+"\r")
+	subnet2 := "subnet-2"
+	input(b, subnet2+"\r")
+	subnet3 := "subnet-3"
+	input(b, subnet3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptPrivateSubnet()
+
+	assert.NoError(t, err)
+	assert.Equal(t, subnet1, c.Config.Aws.Config.PrivateCustomSubnets[0])
+	assert.Equal(t, subnet2, c.Config.Aws.Config.PrivateCustomSubnets[1])
+	assert.Equal(t, subnet3, c.Config.Aws.Config.PrivateCustomSubnets[2])
+}
+
+func TestPromptPublicSubnet(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	enablePublicSubnets := true
+	input(b, "\r")
+
+	subnet1 := "172.168.192.1"
+	input(b, subnet1+"\r")
+	subnet2 := "172.168.192.2"
+	input(b, subnet2+"\r")
+	subnet3 := "172.168.192.3"
+	input(b, subnet3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptPublicSubnet()
+
+	assert.NoError(t, err)
+	assert.Equal(t, enablePublicSubnets, len(c.Config.Aws.Config.PublicCustomSubnets) > 0)
+	assert.Equal(t, subnet1, c.Config.Aws.Config.PublicCustomSubnets[0])
+	assert.Equal(t, subnet2, c.Config.Aws.Config.PublicCustomSubnets[1])
+	assert.Equal(t, subnet3, c.Config.Aws.Config.PublicCustomSubnets[2])
+}
+
+func TestPromptPgCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	noDefaultAwsCerts := true
+	input(b, moveDown+"\r")
+
+	certFilePath := "test.pem"
+	certFileContent := "POSTGRESQL CERT"
+	input(b, certFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == certFilePath {
+				data = []byte(certFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == certFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptPgCert()
+
+	assert.NoError(t, err)
+	assert.Equal(t, noDefaultAwsCerts, c.Config.Aws.Config.ManagedRdsCertificate != "")
+	assert.Equal(t, certFileContent, c.Config.Aws.Config.ManagedRdsCertificate)
+}
+
+func TestPromptOsCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	noDefaultAwsCerts := true
+	input(b, moveDown+"\r")
+
+	certFilePath := "test.pem"
+	certFileContent := "OPENSEARCH CERT"
+	input(b, certFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == certFilePath {
+				data = []byte(certFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == certFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptOsCert()
+
+	assert.NoError(t, err)
+	assert.Equal(t, noDefaultAwsCerts, c.Config.Aws.Config.ManagedOpensearchCertificate != "")
+	assert.Equal(t, certFileContent, c.Config.Aws.Config.ManagedOpensearchCertificate)
+}
+
+func TestPromptsCidrAWSManagedErr(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.Error(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "aws"
+	workspacePath := "/hab/a2_deploy_workspace"
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.Aws.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.Aws.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.Aws.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.Aws.WorkspacePath)
+}
+
+func TestPromptsCidrAWSManagedErr1(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	sshUser := "ec2-user"
+	input(b, sshUser+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.Error(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "aws"
+	workspacePath := "/hab/a2_deploy_workspace"
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.Aws.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.Aws.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.Aws.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.Aws.WorkspacePath)
+
+	assert.Equal(t, sshUser, c.Config.Architecture.Aws.SSHUser)
+}
+
+func TestPromptsCidrAWSManagedErr2(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	sshUser := "ec2-user"
+	input(b, sshUser+"\r")
+
+	sshGroup := "ec2-user"
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.Error(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "aws"
+	workspacePath := "/hab/a2_deploy_workspace"
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.Aws.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.Aws.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.Aws.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.Aws.WorkspacePath)
+
+	assert.Equal(t, sshUser, c.Config.Architecture.Aws.SSHUser)
+	assert.Equal(t, sshGroup, c.Config.Architecture.Aws.SSHGroupName)
+}
+
+func TestPromptsCidrAWSManagedErr3(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	sshUser := "ec2-user"
+	input(b, sshUser+"\r")
+
+	sshGroup := "ec2-user"
+	input(b, "\r")
+
+	sshPort := 22
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.Error(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "aws"
+	workspacePath := "/hab/a2_deploy_workspace"
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.Aws.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.Aws.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.Aws.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.Aws.WorkspacePath)
+
+	assert.Equal(t, sshUser, c.Config.Architecture.Aws.SSHUser)
+	assert.Equal(t, sshGroup, c.Config.Architecture.Aws.SSHGroupName)
+	assert.Equal(t, fmt.Sprint(sshPort), c.Config.Architecture.Aws.SSHPort)
+}
+
+func TestBatchRunErr(t *testing.T) {
+	for i := 0; i <= 47; i++ {
+		testPromptsCidrAWSManagedErrRun(t, i)
+	}
+}
+
+func testPromptsCidrAWSManagedErrRun(t *testing.T, upto int) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	callInput(upto, b)
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == "test.pem" {
+				data = []byte("test.pem")
+			} else if filepath == "automateFqdnRootCa.pem" {
+				data = []byte("automateFqdnRootCa.pem")
+			} else if filepath == "chefInfraServerFqdnRootCa.pem" {
+				data = []byte("chefInfraServerFqdnRootCa.pem")
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" ||
+				name == "automateFqdnRootCa.pem" ||
+				name == "chefInfraServerFqdnRootCa.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.Error(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "aws"
+	workspacePath := "/hab/a2_deploy_workspace"
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.Aws.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.Aws.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.Aws.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.Aws.WorkspacePath)
+}
+
+func callInput(upto int, b *bytes.Buffer) {
+	if upto >= 1 {
+		input(b, "ec2-user"+"\r")
+	}
+	if upto >= 2 {
+		input(b, "\r")
+	}
+	if upto >= 3 {
+		input(b, "\r")
+	}
+	if upto >= 4 {
+		input(b, "test.pem"+"\r")
+	}
+	if upto >= 5 {
+		input(b, "\r")
+	}
+	if upto >= 6 {
+		input(b, "\r")
+	}
+	if upto >= 7 {
+		input(b, "asia"+moveDown+"\r")
+	}
+	if upto >= 8 {
+		input(b, "vpc-123"+"\r")
+	}
+	if upto >= 9 {
+		input(b, "\r")
+	}
+	if upto >= 10 {
+		input(b, "172.168.192.0"+"\r")
+	}
+	if upto >= 11 {
+		input(b, "my-key-pair"+"\r")
+	}
+	if upto >= 12 {
+		input(b, "ami-123"+"\r")
+	}
+	if upto >= 13 {
+		input(b, "\r")
+	}
+	if upto >= 14 {
+		input(b, "\r")
+	}
+	if upto >= 15 {
+		input(b, "A2-automate.amazonaws.com"+"\r")
+	}
+	if upto >= 16 {
+		input(b, "arn-123"+"\r")
+	}
+	if upto >= 17 {
+		input(b, "automateFqdnRootCa.pem"+"\r")
+	}
+	if upto >= 18 {
+		input(b, "admin12345"+"\r")
+	}
+	if upto >= 19 {
+		input(b, fmt.Sprint(1)+"\r")
+	}
+	if upto >= 20 {
+		input(b, "\r")
+	}
+	if upto >= 21 {
+		input(b, "\r")
+	}
+	if upto >= 22 {
+		input(b, "\r")
+	}
+	if upto >= 23 {
+		input(b, "\r")
+	}
+	if upto >= 24 {
+		input(b, "chef-infra-server.amazonaws.com"+"\r")
+	}
+	if upto >= 25 {
+		input(b, "arn-124"+"\r")
+	}
+	if upto >= 26 {
+		input(b, "chefInfraServerFqdnRootCa.pem"+"\r")
+	}
+	if upto >= 27 {
+		input(b, fmt.Sprint(1)+"\r")
+	}
+	if upto >= 28 {
+		input(b, "\r")
+	}
+	if upto >= 29 {
+		input(b, "\r")
+	}
+	if upto >= 30 {
+		input(b, "\r")
+	}
+	if upto >= 31 {
+		input(b, "\r")
+	}
+	if upto >= 32 {
+		input(b, "\r")
+	}
+	if upto >= 33 {
+		input(b, "opensearch-automate"+"\r")
+	}
+	if upto >= 34 {
+		input(b, "opensearch.automate.com"+"\r")
+	}
+	if upto >= 35 {
+		input(b, "admin"+"\r")
+	}
+	if upto >= 36 {
+		input(b, "admin@1234"+"\r")
+	}
+	if upto >= 37 {
+		input(b, "\r")
+	}
+	if upto >= 38 {
+		input(b, "arn-os-snapshot-role"+"\r")
+	}
+	if upto >= 39 {
+		input(b, "ASKEIDSKFLDKDFJERKDS"+"\r")
+	}
+	if upto >= 40 {
+		input(b, "asdfelkjsldfklsdjflsdfjdklsfjsdfsldkfjsd"+"\r")
+	}
+	if upto >= 41 {
+		input(b, "pg.automate.com:5432"+"\r")
+	}
+	if upto >= 42 {
+		input(b, "root"+"\r")
+	}
+	if upto >= 43 {
+		input(b, "root1234"+"\r")
+	}
+	if upto >= 44 {
+		input(b, "dbUser"+"\r")
+	}
+	if upto >= 45 {
+		input(b, "dbUser1234"+"\r")
+	}
+	if upto >= 46 {
+		input(b, "\r")
+	}
+	if upto >= 47 {
+		input(b, "\r")
+	}
+	if upto >= 48 {
+		input(b, "automate-backup"+"\r")
+	}
+}
+
+func TestPromptCidr(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+	priSub1 := "subnet-pri-1"
+	input(b, priSub1+"\r")
+	priSub2 := "subnet-pri-2"
+	input(b, priSub2+"\r")
+	priSub3 := "subnet-pri-3"
+	input(b, priSub3+"\r")
+
+	input(b, "\r")
+	pubSub1 := "subnet-pub-1"
+	input(b, pubSub1+"\r")
+	pubSub2 := "subnet-pub-2"
+	input(b, pubSub2+"\r")
+	pubSub3 := "subnet-pub-3"
+	input(b, pubSub3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.NoError(t, err)
+	assert.Equal(t, 3, len(c.Config.Aws.Config.PrivateCustomSubnets))
+	assert.Equal(t, priSub1, c.Config.Aws.Config.PrivateCustomSubnets[0])
+	assert.Equal(t, priSub2, c.Config.Aws.Config.PrivateCustomSubnets[1])
+	assert.Equal(t, priSub3, c.Config.Aws.Config.PrivateCustomSubnets[2])
+
+	assert.Equal(t, 3, len(c.Config.Aws.Config.PublicCustomSubnets))
+	assert.Equal(t, pubSub1, c.Config.Aws.Config.PublicCustomSubnets[0])
+	assert.Equal(t, pubSub2, c.Config.Aws.Config.PublicCustomSubnets[1])
+	assert.Equal(t, pubSub3, c.Config.Aws.Config.PublicCustomSubnets[2])
+}
+
+func TestPromptCidrErr1(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestPromptCidrErr2(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestPromptCidrErr3(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+	priSub1 := "subnet-pri-1"
+	input(b, priSub1+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestPromptCidrErr4(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+	priSub1 := "subnet-pri-1"
+	input(b, priSub1+"\r")
+	priSub2 := "subnet-pri-2"
+	input(b, priSub2+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestPromptCidrErr5(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+	priSub1 := "subnet-pri-1"
+	input(b, priSub1+"\r")
+	priSub2 := "subnet-pri-2"
+	input(b, priSub2+"\r")
+	priSub3 := "subnet-pri-3"
+	input(b, priSub3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestPromptCidrErr6(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+	priSub1 := "subnet-pri-1"
+	input(b, priSub1+"\r")
+	priSub2 := "subnet-pri-2"
+	input(b, priSub2+"\r")
+	priSub3 := "subnet-pri-3"
+	input(b, priSub3+"\r")
+
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestPromptCidrErr7(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+	priSub1 := "subnet-pri-1"
+	input(b, priSub1+"\r")
+	priSub2 := "subnet-pri-2"
+	input(b, priSub2+"\r")
+	priSub3 := "subnet-pri-3"
+	input(b, priSub3+"\r")
+
+	input(b, "\r")
+	pubSub1 := "subnet-pub-1"
+	input(b, pubSub1+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestPromptCidrErr8(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+	priSub1 := "subnet-pri-1"
+	input(b, priSub1+"\r")
+	priSub2 := "subnet-pri-2"
+	input(b, priSub2+"\r")
+	priSub3 := "subnet-pri-3"
+	input(b, priSub3+"\r")
+
+	input(b, "\r")
+	pubSub1 := "subnet-pub-1"
+	input(b, pubSub1+"\r")
+	pubSub2 := "subnet-pub-2"
+	input(b, pubSub2+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfig{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptCidr()
+
+	assert.Error(t, err)
+}
+
+func TestAwsHaProvisionConfigFactory(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := AwsHaProvisionConfigFactory(p)
+
+	assert.NotNil(t, c)
+}
diff --git a/lib/config/genconfig/awsregions_test.go b/lib/config/genconfig/awsregions_test.go
new file mode 100644
index 000000000..cf41f97a6
--- /dev/null
+++ b/lib/config/genconfig/awsregions_test.go
@@ -0,0 +1,47 @@
+package genconfig
+
+import (
+	"bytes"
+	"errors"
+	"io"
+	"testing"
+
+	"github.com/bmizerany/assert"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/ioutils"
+	"github.com/chef/automate/lib/pmt"
+)
+
+const esc = "\033["
+const moveDown = esc + "1B"
+const moveUp = esc + "1A"
+const selectKey = "\r"
+const clearLine = esc + "2K\n"
+
+func TestGetAwsRegionSuccessCase(t *testing.T) {
+	inputBytes := []byte(moveDown + moveDown + selectKey)
+	in := io.NopCloser(bytes.NewBuffer(inputBytes))
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	mfsu := &fileutils.MockFileSystemUtils{}
+	p := pmt.PromptFactory(in, out, mfsu)
+	awsRegions := AwsRegionsImpFactory(p)
+	val, err := awsRegions.Choose("AWS Region of bucket")
+
+	assert.Equal(t, nil, err)
+	assert.Equal(t, "us-west-1", val)
+}
+
+func TestGetAwsRegionErrorCase(t *testing.T) {
+	inputBytes := []byte(moveDown + moveDown)
+	in := io.NopCloser(bytes.NewBuffer(inputBytes))
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	mfsu := &fileutils.MockFileSystemUtils{}
+	p := pmt.PromptFactory(in, out, mfsu)
+	awsRegions := AwsRegionsImpFactory(p)
+	val, err := awsRegions.Choose("AWS Region of bucket")
+
+	assert.Equal(t, errors.New("prompt failed ^D"), err)
+	assert.Equal(t, "", val)
+}
diff --git a/lib/config/genconfig/hadeployconfig_test.go b/lib/config/genconfig/hadeployconfig_test.go
new file mode 100644
index 000000000..ee00d71a7
--- /dev/null
+++ b/lib/config/genconfig/hadeployconfig_test.go
@@ -0,0 +1,1425 @@
+package genconfig
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"testing"
+
+	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/ioutils"
+	"github.com/chef/automate/lib/pmt"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPromptsOnPremManaged(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	sshUser := "ec2-user"
+	input(b, sshUser+"\r")
+
+	sshGroup := "ec2-user"
+	input(b, "\r")
+
+	sshPort := 22
+	input(b, "\r")
+
+	sshKeyFilePath := "test.pem"
+	sshKeyFileContent := "ssh user private key"
+	input(b, sshKeyFilePath+"\r")
+
+	enableCustomCerts := false
+	input(b, "\r")
+
+	automateFqdn := "A2-automate.amazonaws.com"
+	input(b, automateFqdn+"\r")
+
+	automateFqdnRootCaFilePath := "automateFqdnRootCa.pem"
+	automateFqdnRootCaFileContent := "Automate Root CA"
+	input(b, automateFqdnRootCaFilePath+"\r")
+
+	automateAdminPass := "admin12345"
+	input(b, automateAdminPass+"\r")
+
+	automateNodeCount := 1
+	input(b, fmt.Sprint(automateNodeCount)+"\r")
+
+	automateIp1 := "172.168.192.1"
+	input(b, automateIp1+"\r")
+
+	chefInfraServerFqdn := "chef-infra-server.amazonaws.com"
+	input(b, chefInfraServerFqdn+"\r")
+
+	chefInfraServerFqdnRootCaFilePath := "chefInfraServerFqdnRootCa.pem"
+	chefInfraServerFqdnRootCaFileContent := "Chef Infra Server Root CA"
+	input(b, chefInfraServerFqdnRootCaFilePath+"\r")
+
+	chefInfraServerNodeCount := 1
+	input(b, fmt.Sprint(chefInfraServerNodeCount)+"\r")
+
+	chefInfraServerIp1 := "172.168.192.4"
+	input(b, chefInfraServerIp1+"\r")
+
+	useExternalServices := true
+	input(b, "\r")
+
+	useAwsDatabases := true
+	input(b, "\r")
+
+	osDomainName := "opensearch-automate"
+	input(b, osDomainName+"\r")
+
+	osDomainUrl := "opensearch.automate.com"
+	input(b, osDomainUrl+"\r")
+
+	osUser := "admin"
+	input(b, osUser+"\r")
+
+	osPass := "admin@1234"
+	input(b, osPass+"\r")
+
+	osDefaultCerts := true
+	input(b, "\r")
+
+	osSnapshotRoleArn := "arn-os-snapshot-role"
+	input(b, osSnapshotRoleArn+"\r")
+
+	osSnapshotUserAccessKeyId := "ASKEIDSKFLDKDFJERKDS"
+	input(b, osSnapshotUserAccessKeyId+"\r")
+
+	osSnapshotUserAccessKeySecret := "asdfelkjsldfklsdjflsdfjdklsfjsdfsldkfjsd"
+	input(b, osSnapshotUserAccessKeySecret+"\r")
+
+	pgUrl := "pg.automate.com:5432"
+	input(b, pgUrl+"\r")
+
+	pgSuperUserName := "root"
+	input(b, pgSuperUserName+"\r")
+
+	pgSuperUserPass := "root1234"
+	input(b, pgSuperUserPass+"\r")
+
+	pgDbUserName := "dbUser"
+	input(b, pgDbUserName+"\r")
+
+	pgDbUserPass := "dbUser1234"
+	input(b, pgDbUserPass+"\r")
+
+	pgDefaultCerts := true
+	input(b, "\r")
+
+	enableBackup := true
+	input(b, "\r")
+
+	useAwsS3 := true
+	input(b, "\r")
+
+	awsBackupBucketName := "automate-backup"
+	input(b, awsBackupBucketName+"\r")
+
+	awsBackupAcessKeyId := "AKEKFKEIDKETODDFEDFE"
+	input(b, awsBackupAcessKeyId+"\r")
+
+	awsBackupAcessKeySecret := "sdfsdflsdlfkjsdlkjeflilsfldijflsdkfjddfd"
+	input(b, awsBackupAcessKeySecret+"\r")
+
+	awsRegion := "ap-south-2"
+	input(b, "asia"+moveDown+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == sshKeyFilePath {
+				data = []byte(sshKeyFileContent)
+			} else if filepath == automateFqdnRootCaFilePath {
+				data = []byte(automateFqdnRootCaFileContent)
+			} else if filepath == chefInfraServerFqdnRootCaFilePath {
+				data = []byte(chefInfraServerFqdnRootCaFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == sshKeyFilePath ||
+				name == automateFqdnRootCaFilePath ||
+				name == chefInfraServerFqdnRootCaFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.NoError(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "existing_nodes"
+	workspacePath := "/hab/a2_deploy_workspace"
+	automateConfigFile := "configs/automate.toml"
+	pgInstanceCount := 0
+	osInstanceCount := 0
+	s3Endpoint := "https://s3.amazonaws.com"
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.ExistingInfra.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.ExistingInfra.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.ExistingInfra.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.ExistingInfra.WorkspacePath)
+
+	assert.Equal(t, sshUser, c.Config.Architecture.ExistingInfra.SSHUser)
+	assert.Equal(t, sshGroup, c.Config.Architecture.ExistingInfra.SSHGroupName)
+	assert.Equal(t, fmt.Sprint(sshPort), c.Config.Architecture.ExistingInfra.SSHPort)
+	assert.Equal(t, sshKeyFilePath, c.Config.Architecture.ExistingInfra.SSHKeyFile)
+
+	assert.Equal(t, enableCustomCerts, c.HasCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.Automate.Config.EnableCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.ChefServer.Config.EnableCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.Postgresql.Config.EnableCustomCerts)
+	assert.Equal(t, enableCustomCerts, c.Config.Opensearch.Config.EnableCustomCerts)
+
+	assert.Equal(t, automateFqdn, c.Config.Automate.Config.Fqdn)
+	assert.Equal(t, automateFqdnRootCaFileContent, c.Config.Automate.Config.FqdnRootCA)
+	assert.Equal(t, automateConfigFile, c.Config.Automate.Config.ConfigFile)
+	assert.Equal(t, automateAdminPass, c.Config.Automate.Config.AdminPassword)
+	assert.Equal(t, fmt.Sprint(automateNodeCount), c.Config.Automate.Config.InstanceCount)
+	assert.Equal(t, automateNodeCount, len(c.Config.ExistingInfra.Config.AutomatePrivateIps))
+	assert.Equal(t, automateIp1, c.Config.ExistingInfra.Config.AutomatePrivateIps[0])
+
+	assert.Equal(t, chefInfraServerFqdn, c.Config.ChefServer.Config.ChefServerFqdn)
+	assert.Equal(t, chefInfraServerFqdnRootCaFileContent, c.Config.ChefServer.Config.FqdnRootCA)
+	assert.Equal(t, fmt.Sprint(chefInfraServerNodeCount), c.Config.ChefServer.Config.InstanceCount)
+	assert.Equal(t, chefInfraServerNodeCount, len(c.Config.ExistingInfra.Config.ChefServerPrivateIps))
+	assert.Equal(t, chefInfraServerIp1, c.Config.ExistingInfra.Config.ChefServerPrivateIps[0])
+
+	assert.Equal(t, useExternalServices, c.Config.External.Database != nil)
+	assert.Equal(t, useAwsDatabases, c.Config.External.Database.Type == "aws")
+
+	assert.Equal(t, osDomainName, c.Config.External.Database.OpenSearch.OpensearchDomainName)
+	assert.Equal(t, osDomainUrl, c.Config.External.Database.OpenSearch.OpensearchDomainURL)
+	assert.Equal(t, osUser, c.Config.External.Database.OpenSearch.OpensearchUsername)
+	assert.Equal(t, osPass, c.Config.External.Database.OpenSearch.OpensearchUserPassword)
+	assert.Equal(t, osDefaultCerts, len(c.Config.External.Database.OpenSearch.OpensearchRootCert) == 0)
+	assert.Equal(t, osSnapshotRoleArn, c.Config.External.Database.OpenSearch.Aws.AwsOsSnapshotRoleArn)
+	assert.Equal(t, osSnapshotUserAccessKeyId, c.Config.External.Database.OpenSearch.Aws.OsSnapshotUserAccessKeyID)
+	assert.Equal(t, osSnapshotUserAccessKeySecret, c.Config.External.Database.OpenSearch.Aws.OsSnapshotUserAccessKeySecret)
+
+	assert.Equal(t, pgUrl, c.Config.External.Database.PostgreSQL.InstanceURL)
+	assert.Equal(t, pgSuperUserName, c.Config.External.Database.PostgreSQL.SuperuserUsername)
+	assert.Equal(t, pgSuperUserPass, c.Config.External.Database.PostgreSQL.SuperuserPassword)
+	assert.Equal(t, pgDbUserName, c.Config.External.Database.PostgreSQL.DbuserUsername)
+	assert.Equal(t, pgDbUserPass, c.Config.External.Database.PostgreSQL.DbuserPassword)
+	assert.Equal(t, pgDefaultCerts, len(c.Config.External.Database.PostgreSQL.PostgresqlRootCert) == 0)
+
+	assert.Equal(t, fmt.Sprint(osInstanceCount), c.Config.Opensearch.Config.InstanceCount)
+	assert.Equal(t, fmt.Sprint(pgInstanceCount), c.Config.Postgresql.Config.InstanceCount)
+
+	assert.Equal(t, enableBackup, c.Config.Architecture.ExistingInfra.BackupMount == "/mnt/automate_backups")
+	assert.Equal(t, enableBackup, len(c.Config.Architecture.ExistingInfra.BackupConfig) > 0)
+	assert.Equal(t, useAwsS3, c.Config.Architecture.ExistingInfra.BackupConfig == "object_storage")
+	assert.Equal(t, useAwsS3, len(c.Config.ObjectStorage.Config.BucketName) > 0)
+	assert.Equal(t, awsBackupBucketName, c.Config.ObjectStorage.Config.BucketName)
+	assert.Equal(t, awsBackupAcessKeyId, c.Config.ObjectStorage.Config.AccessKey)
+	assert.Equal(t, awsBackupAcessKeySecret, c.Config.ObjectStorage.Config.SecretKey)
+	assert.Equal(t, s3Endpoint, c.Config.ObjectStorage.Config.Endpoint)
+	assert.Equal(t, awsRegion, c.Config.ObjectStorage.Config.Region)
+}
+
+func TestOnPremPromptOpenSearchNodesCustomCertPerNode(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := true
+	input(b, moveDown+"\r")
+
+	rootCaFilePath := "rootca.pem"
+	rootCaFileContent := "ROOT CA"
+	input(b, rootCaFilePath+"\r")
+
+	adminCertFilePath := "admin.pem"
+	adminCertFileContent := "ADMIN CERT"
+	input(b, adminCertFilePath+"\r")
+
+	adminKeyFilePath := "admin.key"
+	adminKeyFileContent := "ADMIN KEY"
+	input(b, adminKeyFilePath+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	priKeyFilePath2 := "test2.key"
+	priKeyFileContent2 := "PRIVATE KEY2"
+	input(b, priKeyFilePath2+"\r")
+
+	pubKeyFilePath2 := "test2.pem"
+	pubKeyFileContent2 := "PUBLIC KEY2"
+	input(b, pubKeyFilePath2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	priKeyFilePath3 := "test3.key"
+	priKeyFileContent3 := "PRIVATE KEY3"
+	input(b, priKeyFilePath3+"\r")
+
+	pubKeyFilePath3 := "test3.pem"
+	pubKeyFileContent3 := "PUBLIC KEY3"
+	input(b, pubKeyFilePath3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			} else if filepath == priKeyFilePath2 {
+				data = []byte(priKeyFileContent2)
+			} else if filepath == pubKeyFilePath2 {
+				data = []byte(pubKeyFileContent2)
+			} else if filepath == priKeyFilePath3 {
+				data = []byte(priKeyFileContent3)
+			} else if filepath == pubKeyFilePath3 {
+				data = []byte(pubKeyFileContent3)
+			} else if filepath == rootCaFilePath {
+				data = []byte(rootCaFileContent)
+			} else if filepath == adminCertFilePath {
+				data = []byte(adminCertFileContent)
+			} else if filepath == adminKeyFilePath {
+				data = []byte(adminKeyFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 ||
+				name == priKeyFilePath2 ||
+				name == pubKeyFilePath2 ||
+				name == priKeyFilePath3 ||
+				name == pubKeyFilePath3 ||
+				name == rootCaFilePath ||
+				name == adminCertFilePath ||
+				name == adminKeyFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptOpenSearchNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.Opensearch.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.Opensearch.Config.EnableCustomCerts)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Opensearch.Config.PublicKey) == 0)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Opensearch.Config.PrivateKey) == 0)
+	assert.Equal(t, rootCaFileContent, c.Config.Opensearch.Config.RootCA)
+	assert.Equal(t, adminCertFileContent, c.Config.Opensearch.Config.AdminCert)
+	assert.Equal(t, adminKeyFileContent, c.Config.Opensearch.Config.AdminKey)
+
+	assert.Equal(t, enableNodeCustomCerts, len(*c.Config.Opensearch.Config.CertsByIP) > 0)
+	assert.Equal(t, instanceCount, len(*c.Config.Opensearch.Config.CertsByIP))
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.OpensearchPrivateIps))
+
+	assert.Equal(t, ip1, (*c.Config.Opensearch.Config.CertsByIP)[0].IP)
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.OpensearchPrivateIps[0])
+	assert.Equal(t, priKeyFileContent1, (*c.Config.Opensearch.Config.CertsByIP)[0].PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, (*c.Config.Opensearch.Config.CertsByIP)[0].PublicKey)
+
+	assert.Equal(t, ip2, (*c.Config.Opensearch.Config.CertsByIP)[1].IP)
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.OpensearchPrivateIps[1])
+	assert.Equal(t, priKeyFileContent2, (*c.Config.Opensearch.Config.CertsByIP)[1].PrivateKey)
+	assert.Equal(t, pubKeyFileContent2, (*c.Config.Opensearch.Config.CertsByIP)[1].PublicKey)
+
+	assert.Equal(t, ip3, (*c.Config.Opensearch.Config.CertsByIP)[2].IP)
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.OpensearchPrivateIps[2])
+	assert.Equal(t, priKeyFileContent3, (*c.Config.Opensearch.Config.CertsByIP)[2].PrivateKey)
+	assert.Equal(t, pubKeyFileContent3, (*c.Config.Opensearch.Config.CertsByIP)[2].PublicKey)
+}
+
+func TestOnPremPromptOpenSearchNodesCustomCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := false
+	input(b, "\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	rootCaFilePath := "rootca.pem"
+	rootCaFileContent := "ROOT CA"
+	input(b, rootCaFilePath+"\r")
+
+	adminCertFilePath := "admin.pem"
+	adminCertFileContent := "ADMIN CERT"
+	input(b, adminCertFilePath+"\r")
+
+	adminKeyFilePath := "admin.key"
+	adminKeyFileContent := "ADMIN KEY"
+	input(b, adminKeyFilePath+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			} else if filepath == rootCaFilePath {
+				data = []byte(rootCaFileContent)
+			} else if filepath == adminCertFilePath {
+				data = []byte(adminCertFileContent)
+			} else if filepath == adminKeyFilePath {
+				data = []byte(adminKeyFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 ||
+				name == rootCaFilePath ||
+				name == adminCertFilePath ||
+				name == adminKeyFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptOpenSearchNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.Opensearch.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.Opensearch.Config.EnableCustomCerts)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Opensearch.Config.PublicKey) == 0)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Opensearch.Config.PrivateKey) == 0)
+	assert.Equal(t, priKeyFileContent1, c.Config.Opensearch.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, c.Config.Opensearch.Config.PublicKey)
+	assert.Equal(t, rootCaFileContent, c.Config.Opensearch.Config.RootCA)
+	assert.Equal(t, adminCertFileContent, c.Config.Opensearch.Config.AdminCert)
+	assert.Equal(t, adminKeyFileContent, c.Config.Opensearch.Config.AdminKey)
+
+	assert.Equal(t, true, c.Config.Opensearch.Config.CertsByIP == nil)
+
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.OpensearchPrivateIps))
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.OpensearchPrivateIps[0])
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.OpensearchPrivateIps[1])
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.OpensearchPrivateIps[2])
+}
+
+func TestOnPremPromptPostgresqlCustomCertPerNode(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := true
+	input(b, moveDown+"\r")
+
+	rootCaFilePath := "rootca.pem"
+	rootCaFileContent := "ROOT CA"
+	input(b, rootCaFilePath+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	priKeyFilePath2 := "test2.key"
+	priKeyFileContent2 := "PRIVATE KEY2"
+	input(b, priKeyFilePath2+"\r")
+
+	pubKeyFilePath2 := "test2.pem"
+	pubKeyFileContent2 := "PUBLIC KEY2"
+	input(b, pubKeyFilePath2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	priKeyFilePath3 := "test3.key"
+	priKeyFileContent3 := "PRIVATE KEY3"
+	input(b, priKeyFilePath3+"\r")
+
+	pubKeyFilePath3 := "test3.pem"
+	pubKeyFileContent3 := "PUBLIC KEY3"
+	input(b, pubKeyFilePath3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			} else if filepath == priKeyFilePath2 {
+				data = []byte(priKeyFileContent2)
+			} else if filepath == pubKeyFilePath2 {
+				data = []byte(pubKeyFileContent2)
+			} else if filepath == priKeyFilePath3 {
+				data = []byte(priKeyFileContent3)
+			} else if filepath == pubKeyFilePath3 {
+				data = []byte(pubKeyFileContent3)
+			} else if filepath == rootCaFilePath {
+				data = []byte(rootCaFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 ||
+				name == priKeyFilePath2 ||
+				name == pubKeyFilePath2 ||
+				name == priKeyFilePath3 ||
+				name == pubKeyFilePath3 ||
+				name == rootCaFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptPostgresql()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.Postgresql.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.Postgresql.Config.EnableCustomCerts)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Postgresql.Config.PublicKey) == 0)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Postgresql.Config.PrivateKey) == 0)
+	assert.Equal(t, rootCaFileContent, c.Config.Postgresql.Config.RootCA)
+
+	assert.Equal(t, enableNodeCustomCerts, len(*c.Config.Postgresql.Config.CertsByIP) > 0)
+	assert.Equal(t, instanceCount, len(*c.Config.Postgresql.Config.CertsByIP))
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.PostgresqlPrivateIps))
+
+	assert.Equal(t, ip1, (*c.Config.Postgresql.Config.CertsByIP)[0].IP)
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.PostgresqlPrivateIps[0])
+	assert.Equal(t, priKeyFileContent1, (*c.Config.Postgresql.Config.CertsByIP)[0].PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, (*c.Config.Postgresql.Config.CertsByIP)[0].PublicKey)
+
+	assert.Equal(t, ip2, (*c.Config.Postgresql.Config.CertsByIP)[1].IP)
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.PostgresqlPrivateIps[1])
+	assert.Equal(t, priKeyFileContent2, (*c.Config.Postgresql.Config.CertsByIP)[1].PrivateKey)
+	assert.Equal(t, pubKeyFileContent2, (*c.Config.Postgresql.Config.CertsByIP)[1].PublicKey)
+
+	assert.Equal(t, ip3, (*c.Config.Postgresql.Config.CertsByIP)[2].IP)
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.PostgresqlPrivateIps[2])
+	assert.Equal(t, priKeyFileContent3, (*c.Config.Postgresql.Config.CertsByIP)[2].PrivateKey)
+	assert.Equal(t, pubKeyFileContent3, (*c.Config.Postgresql.Config.CertsByIP)[2].PublicKey)
+}
+
+func TestOnPremPromptPostgresqlCustomCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := false
+	input(b, "\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	rootCaFilePath := "rootca.pem"
+	rootCaFileContent := "ROOT CA"
+	input(b, rootCaFilePath+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			} else if filepath == rootCaFilePath {
+				data = []byte(rootCaFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 ||
+				name == rootCaFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptPostgresql()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.Postgresql.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.Postgresql.Config.EnableCustomCerts)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Postgresql.Config.PublicKey) == 0)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Postgresql.Config.PrivateKey) == 0)
+	assert.Equal(t, priKeyFileContent1, c.Config.Postgresql.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, c.Config.Postgresql.Config.PublicKey)
+	assert.Equal(t, rootCaFileContent, c.Config.Postgresql.Config.RootCA)
+
+	assert.Equal(t, true, c.Config.Postgresql.Config.CertsByIP == nil)
+
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.PostgresqlPrivateIps))
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.PostgresqlPrivateIps[0])
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.PostgresqlPrivateIps[1])
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.PostgresqlPrivateIps[2])
+}
+
+func TestOnPremPromptAutomateNodesCustomCertPerNode(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := true
+	input(b, moveDown+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	priKeyFilePath2 := "test2.key"
+	priKeyFileContent2 := "PRIVATE KEY2"
+	input(b, priKeyFilePath2+"\r")
+
+	pubKeyFilePath2 := "test2.pem"
+	pubKeyFileContent2 := "PUBLIC KEY2"
+	input(b, pubKeyFilePath2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	priKeyFilePath3 := "test3.key"
+	priKeyFileContent3 := "PRIVATE KEY3"
+	input(b, priKeyFilePath3+"\r")
+
+	pubKeyFilePath3 := "test3.pem"
+	pubKeyFileContent3 := "PUBLIC KEY3"
+	input(b, pubKeyFilePath3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			} else if filepath == priKeyFilePath2 {
+				data = []byte(priKeyFileContent2)
+			} else if filepath == pubKeyFilePath2 {
+				data = []byte(pubKeyFileContent2)
+			} else if filepath == priKeyFilePath3 {
+				data = []byte(priKeyFileContent3)
+			} else if filepath == pubKeyFilePath3 {
+				data = []byte(pubKeyFileContent3)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 ||
+				name == priKeyFilePath2 ||
+				name == pubKeyFilePath2 ||
+				name == priKeyFilePath3 ||
+				name == pubKeyFilePath3 {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptAutomateNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.Automate.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.Automate.Config.EnableCustomCerts)
+	assert.Equal(t, "", c.Config.Automate.Config.PublicKey)
+	assert.Equal(t, "", c.Config.Automate.Config.PrivateKey)
+
+	assert.Equal(t, enableNodeCustomCerts, len(*c.Config.Automate.Config.CertsByIP) > 0)
+	assert.Equal(t, instanceCount, len(*c.Config.Automate.Config.CertsByIP))
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.AutomatePrivateIps))
+
+	assert.Equal(t, ip1, (*c.Config.Automate.Config.CertsByIP)[0].IP)
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.AutomatePrivateIps[0])
+	assert.Equal(t, priKeyFileContent1, (*c.Config.Automate.Config.CertsByIP)[0].PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, (*c.Config.Automate.Config.CertsByIP)[0].PublicKey)
+
+	assert.Equal(t, ip2, (*c.Config.Automate.Config.CertsByIP)[1].IP)
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.AutomatePrivateIps[1])
+	assert.Equal(t, priKeyFileContent2, (*c.Config.Automate.Config.CertsByIP)[1].PrivateKey)
+	assert.Equal(t, pubKeyFileContent2, (*c.Config.Automate.Config.CertsByIP)[1].PublicKey)
+
+	assert.Equal(t, ip3, (*c.Config.Automate.Config.CertsByIP)[2].IP)
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.AutomatePrivateIps[2])
+	assert.Equal(t, priKeyFileContent3, (*c.Config.Automate.Config.CertsByIP)[2].PrivateKey)
+	assert.Equal(t, pubKeyFileContent3, (*c.Config.Automate.Config.CertsByIP)[2].PublicKey)
+}
+
+func TestOnPremPromptAutomateNodesCustomCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := false
+	input(b, "\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptAutomateNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.Automate.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.Automate.Config.EnableCustomCerts)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Automate.Config.PublicKey) == 0)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.Automate.Config.PrivateKey) == 0)
+	assert.Equal(t, priKeyFileContent1, c.Config.Automate.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, c.Config.Automate.Config.PublicKey)
+
+	assert.Equal(t, true, c.Config.Automate.Config.CertsByIP == nil)
+
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.AutomatePrivateIps))
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.AutomatePrivateIps[0])
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.AutomatePrivateIps[1])
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.AutomatePrivateIps[2])
+}
+
+func TestOnPremPromptChefInfraServerNodesCustomCertPerNode(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := true
+	input(b, moveDown+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	priKeyFilePath2 := "test2.key"
+	priKeyFileContent2 := "PRIVATE KEY2"
+	input(b, priKeyFilePath2+"\r")
+
+	pubKeyFilePath2 := "test2.pem"
+	pubKeyFileContent2 := "PUBLIC KEY2"
+	input(b, pubKeyFilePath2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	priKeyFilePath3 := "test3.key"
+	priKeyFileContent3 := "PRIVATE KEY3"
+	input(b, priKeyFilePath3+"\r")
+
+	pubKeyFilePath3 := "test3.pem"
+	pubKeyFileContent3 := "PUBLIC KEY3"
+	input(b, pubKeyFilePath3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			} else if filepath == priKeyFilePath2 {
+				data = []byte(priKeyFileContent2)
+			} else if filepath == pubKeyFilePath2 {
+				data = []byte(pubKeyFileContent2)
+			} else if filepath == priKeyFilePath3 {
+				data = []byte(priKeyFileContent3)
+			} else if filepath == pubKeyFilePath3 {
+				data = []byte(pubKeyFileContent3)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 ||
+				name == priKeyFilePath2 ||
+				name == pubKeyFilePath2 ||
+				name == priKeyFilePath3 ||
+				name == pubKeyFilePath3 {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptChefInfraServerNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.ChefServer.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.ChefServer.Config.EnableCustomCerts)
+	assert.Equal(t, "", c.Config.ChefServer.Config.PublicKey)
+	assert.Equal(t, "", c.Config.ChefServer.Config.PrivateKey)
+
+	assert.Equal(t, enableNodeCustomCerts, len(*c.Config.ChefServer.Config.CertsByIP) > 0)
+	assert.Equal(t, instanceCount, len(*c.Config.ChefServer.Config.CertsByIP))
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.ChefServerPrivateIps))
+
+	assert.Equal(t, ip1, (*c.Config.ChefServer.Config.CertsByIP)[0].IP)
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.ChefServerPrivateIps[0])
+	assert.Equal(t, priKeyFileContent1, (*c.Config.ChefServer.Config.CertsByIP)[0].PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, (*c.Config.ChefServer.Config.CertsByIP)[0].PublicKey)
+
+	assert.Equal(t, ip2, (*c.Config.ChefServer.Config.CertsByIP)[1].IP)
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.ChefServerPrivateIps[1])
+	assert.Equal(t, priKeyFileContent2, (*c.Config.ChefServer.Config.CertsByIP)[1].PrivateKey)
+	assert.Equal(t, pubKeyFileContent2, (*c.Config.ChefServer.Config.CertsByIP)[1].PublicKey)
+
+	assert.Equal(t, ip3, (*c.Config.ChefServer.Config.CertsByIP)[2].IP)
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.ChefServerPrivateIps[2])
+	assert.Equal(t, priKeyFileContent3, (*c.Config.ChefServer.Config.CertsByIP)[2].PrivateKey)
+	assert.Equal(t, pubKeyFileContent3, (*c.Config.ChefServer.Config.CertsByIP)[2].PublicKey)
+}
+
+func TestOnPremPromptChefInfraServerNodesCustomCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	instanceCount := 3
+	input(b, fmt.Sprint(instanceCount)+"\r")
+
+	enableCustomCerts := true
+	input(b, "\r")
+
+	enableNodeCustomCerts := false
+	input(b, "\r")
+
+	priKeyFilePath1 := "test1.key"
+	priKeyFileContent1 := "PRIVATE KEY1"
+	input(b, priKeyFilePath1+"\r")
+
+	pubKeyFilePath1 := "test1.pem"
+	pubKeyFileContent1 := "PUBLIC KEY1"
+	input(b, pubKeyFilePath1+"\r")
+
+	ip1 := "172.168.192.1"
+	input(b, ip1+"\r")
+
+	ip2 := "172.168.192.2"
+	input(b, ip2+"\r")
+
+	ip3 := "172.168.192.3"
+	input(b, ip3+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == priKeyFilePath1 {
+				data = []byte(priKeyFileContent1)
+			} else if filepath == pubKeyFilePath1 {
+				data = []byte(pubKeyFileContent1)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == priKeyFilePath1 ||
+				name == pubKeyFilePath1 {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:         p,
+		FileUtils:      mfsu,
+		Config:         &config.HaDeployConfig{},
+		HasCustomCerts: enableCustomCerts,
+	}
+
+	err := c.PromptChefInfraServerNodes()
+
+	assert.NoError(t, err)
+	assert.Equal(t, fmt.Sprint(instanceCount), c.Config.ChefServer.Config.InstanceCount)
+	assert.Equal(t, enableCustomCerts, c.Config.ChefServer.Config.EnableCustomCerts)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.ChefServer.Config.PublicKey) == 0)
+	assert.Equal(t, enableNodeCustomCerts, len(c.Config.ChefServer.Config.PrivateKey) == 0)
+	assert.Equal(t, priKeyFileContent1, c.Config.ChefServer.Config.PrivateKey)
+	assert.Equal(t, pubKeyFileContent1, c.Config.ChefServer.Config.PublicKey)
+
+	assert.Equal(t, true, c.Config.ChefServer.Config.CertsByIP == nil)
+
+	assert.Equal(t, instanceCount, len(c.Config.ExistingInfra.Config.ChefServerPrivateIps))
+	assert.Equal(t, ip1, c.Config.ExistingInfra.Config.ChefServerPrivateIps[0])
+	assert.Equal(t, ip2, c.Config.ExistingInfra.Config.ChefServerPrivateIps[1])
+	assert.Equal(t, ip3, c.Config.ExistingInfra.Config.ChefServerPrivateIps[2])
+}
+
+func TestOnPremPromptBackupMinio(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	enableBackup := true
+	input(b, "\r")
+
+	useMinio := true
+	input(b, moveDown+"\r")
+
+	backupBucketName := "automate-backup"
+	input(b, backupBucketName+"\r")
+
+	backupAcessKeyId := "AKEKFKEIDKETODDFEDFE"
+	input(b, backupAcessKeyId+"\r")
+
+	backupAcessKeySecret := "sdfsdflsdlfkjsdlkjeflilsfldijflsdkfjddfd"
+	input(b, backupAcessKeySecret+"\r")
+
+	backupEndpoint := "https://minio.automate.com"
+	input(b, backupEndpoint+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptBackup()
+
+	assert.NoError(t, err)
+	assert.Equal(t, enableBackup, c.Config.Architecture.ExistingInfra.BackupConfig != "")
+	assert.Equal(t, useMinio, c.Config.Architecture.ExistingInfra.BackupConfig == "object_storage")
+	assert.Equal(t, backupBucketName, c.Config.ObjectStorage.Config.BucketName)
+	assert.Equal(t, backupAcessKeyId, c.Config.ObjectStorage.Config.AccessKey)
+	assert.Equal(t, backupEndpoint, c.Config.ObjectStorage.Config.Endpoint)
+	assert.Equal(t, "/mnt/automate_backups", c.Config.Architecture.ExistingInfra.BackupMount)
+}
+
+func TestOnPremPromptBackupNFS(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	enableBackup := true
+	input(b, "\r")
+
+	useNfs := true
+	input(b, moveDown+moveDown+moveDown+moveDown+"\r")
+
+	backupMountLoc := "/mnt/custom-backup-mount"
+	input(b, backupMountLoc+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.PromptBackup()
+
+	assert.NoError(t, err)
+	assert.Equal(t, enableBackup, c.Config.Architecture.ExistingInfra.BackupConfig != "")
+	assert.Equal(t, useNfs, c.Config.Architecture.ExistingInfra.BackupConfig == "file_system")
+	assert.Equal(t, backupMountLoc, c.Config.Architecture.ExistingInfra.BackupMount)
+}
+func TestOnPremBatchRunErr(t *testing.T) {
+	for i := 0; i <= 35; i++ {
+		testPromptsOnPremManagedErr(t, i)
+	}
+}
+
+func testPromptsOnPremManagedErr(t *testing.T, upto int) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	callInputForHaDeploy(upto, b)
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == "test.pem" {
+				data = []byte("test.pem")
+			} else if filepath == "automateFqdnRootCa.pem" {
+				data = []byte("automateFqdnRootCa.pem")
+			} else if filepath == "chefInfraServerFqdnRootCa.pem" {
+				data = []byte("chefInfraServerFqdnRootCa.pem")
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" ||
+				name == "automateFqdnRootCa.pem" ||
+				name == "chefInfraServerFqdnRootCa.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	err := c.Prompts()
+
+	assert.Error(t, err)
+	secretsKeyFile := "/hab/a2_deploy_workspace/secrets.key"
+	secretsStoreFile := "/hab/a2_deploy_workspace/secrets.json"
+	architecture := "existing_nodes"
+	workspacePath := "/hab/a2_deploy_workspace"
+
+	assert.Equal(t, secretsKeyFile, c.Config.Architecture.ExistingInfra.SecretsKeyFile)
+	assert.Equal(t, secretsStoreFile, c.Config.Architecture.ExistingInfra.SecretsStoreFile)
+	assert.Equal(t, architecture, c.Config.Architecture.ExistingInfra.Architecture)
+	assert.Equal(t, workspacePath, c.Config.Architecture.ExistingInfra.WorkspacePath)
+}
+
+func callInputForHaDeploy(upto int, b *bytes.Buffer) {
+	if upto >= 1 {
+		input(b, "ec2-user"+"\r")
+	}
+	if upto >= 2 {
+		input(b, "\r")
+	}
+	if upto >= 3 {
+		input(b, "\r")
+	}
+	if upto >= 4 {
+		input(b, "test.pem"+"\r")
+	}
+	if upto >= 5 {
+		input(b, "\r")
+	}
+	if upto >= 6 {
+		input(b, "A2-automate.amazonaws.com"+"\r")
+	}
+	if upto >= 7 {
+		input(b, "automateFqdnRootCa.pem"+"\r")
+	}
+	if upto >= 8 {
+		input(b, "admin12345"+"\r")
+	}
+	if upto >= 9 {
+		input(b, fmt.Sprint(1)+"\r")
+	}
+	if upto >= 10 {
+		input(b, "172.168.192.1"+"\r")
+	}
+	if upto >= 11 {
+		input(b, "chef-infra-server.amazonaws.com"+"\r")
+	}
+	if upto >= 12 {
+		input(b, "chefInfraServerFqdnRootCa.pem"+"\r")
+	}
+	if upto >= 13 {
+		input(b, fmt.Sprint(1)+"\r")
+	}
+	if upto >= 14 {
+		input(b, "172.168.192.4"+"\r")
+	}
+	if upto >= 15 {
+		input(b, "\r")
+	}
+	if upto >= 16 {
+		input(b, "\r")
+	}
+	if upto >= 17 {
+		input(b, "opensearch-automate"+"\r")
+	}
+	if upto >= 18 {
+		input(b, "opensearch.automate.com"+"\r")
+	}
+	if upto >= 19 {
+		input(b, "admin"+"\r")
+	}
+	if upto >= 20 {
+		input(b, "admin@1234"+"\r")
+	}
+	if upto >= 21 {
+		input(b, "\r")
+	}
+	if upto >= 22 {
+		input(b, "arn-os-snapshot-role"+"\r")
+	}
+	if upto >= 23 {
+		input(b, "ASKEIDSKFLDKDFJERKDS"+"\r")
+	}
+	if upto >= 24 {
+		input(b, "asdfelkjsldfklsdjflsdfjdklsfjsdfsldkfjsd"+"\r")
+	}
+	if upto >= 25 {
+		input(b, "pg.automate.com:5432"+"\r")
+	}
+	if upto >= 26 {
+		input(b, "root"+"\r")
+	}
+	if upto >= 27 {
+		input(b, "root1234"+"\r")
+	}
+	if upto >= 28 {
+		input(b, "dbUser"+"\r")
+	}
+	if upto >= 29 {
+		input(b, "dbUser1234"+"\r")
+	}
+	if upto >= 30 {
+		input(b, "\r")
+	}
+	if upto >= 31 {
+		input(b, "\r")
+	}
+	if upto >= 32 {
+		input(b, "\r")
+	}
+	if upto >= 33 {
+		input(b, "automate-backup"+"\r")
+	}
+	if upto >= 34 {
+		input(b, "AKEKFKEIDKETODDFEDFE"+"\r")
+	}
+	if upto >= 35 {
+		input(b, "sdfsdflsdlfkjsdlkjeflilsfldijflsdkfjddfd"+"\r")
+	}
+	if upto >= 36 {
+		input(b, "asia"+moveDown+"\r")
+	}
+}
+
+func TestPromptExternalPostgresqlRootCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	input(b, moveDown+"\r")
+
+	certFilePath := "test.pem"
+	certFileContent := "ROOT CA"
+	input(b, certFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == certFilePath {
+				data = []byte(certFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == certFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	c.Config.InitExternal().InitDatabase().Type = "aws"
+
+	err := c.PromptExternalPostgresqlRootCert()
+
+	assert.NoError(t, err)
+
+	assert.Equal(t, certFileContent, c.Config.External.Database.PostgreSQL.PostgresqlRootCert)
+}
+
+func TestPromptExternalOpenSearchRootCert(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+
+	input(b, moveDown+"\r")
+
+	certFilePath := "test.pem"
+	certFileContent := "ROOT CA"
+	input(b, certFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == certFilePath {
+				data = []byte(certFileContent)
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == certFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	c := HaDeployConfigGen{
+		Prompt:    p,
+		FileUtils: mfsu,
+		Config:    &config.HaDeployConfig{},
+	}
+
+	c.Config.InitExternal().InitDatabase().Type = "aws"
+
+	err := c.PromptExternalOpenSearchRootCert()
+
+	assert.NoError(t, err)
+
+	assert.Equal(t, certFileContent, c.Config.External.Database.OpenSearch.OpensearchRootCert)
+}
diff --git a/lib/config/hadeployconfig_test.go b/lib/config/hadeployconfig_test.go
index 633f32ca6..89bddbc9f 100644
--- a/lib/config/hadeployconfig_test.go
+++ b/lib/config/hadeployconfig_test.go
@@ -846,3 +846,159 @@ Dv6bUUXSsZF4fb1diLIBpmD1hh8OGNY65LUPpzAxJeZvo5w=
 		})
 	}
 }
+
+func TestHaDeployConfigInitArchitecture(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitArchitecture()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitAutomate(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitAutomate()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitChefServer(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitChefServer()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitOpenSearch(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitOpenSearch()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitPostgresql(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitPostgresql()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitExistingInfra(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitExistingInfra()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitExternal(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitExternal()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitAws(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitAws()
+	assert.NotNil(t, a)
+}
+
+func TestHaDeployConfigInitObjectStorage(t *testing.T) {
+	c := NewHaDeployConfig()
+	a := c.InitObjectStorage()
+	assert.NotNil(t, a)
+}
+
+func TestObjectStorageInitConfig(t *testing.T) {
+	c := NewHaDeployConfig().InitObjectStorage()
+	a := c.InitConfig()
+	assert.NotNil(t, a)
+}
+
+func TestAwsSettingsInitConfigAwsSettings(t *testing.T) {
+	c := NewHaDeployConfig().InitAws()
+	a := c.InitConfigAwsSettings()
+	assert.NotNil(t, a)
+}
+
+func TestArchitectureInitExistingInfra(t *testing.T) {
+	c := NewHaDeployConfig().InitArchitecture()
+	a := c.InitExistingInfra()
+	assert.NotNil(t, a)
+}
+
+func TestArchitectureInitAws(t *testing.T) {
+	c := NewHaDeployConfig().InitArchitecture()
+	a := c.InitAws()
+	assert.NotNil(t, a)
+}
+
+func TestAutomateSettingsInitConfig(t *testing.T) {
+	c := NewHaDeployConfig().InitAutomate()
+	a := c.InitConfig()
+	assert.NotNil(t, a)
+}
+
+func TestChefServerSettingsInitConfig(t *testing.T) {
+	c := NewHaDeployConfig().InitChefServer()
+	a := c.InitConfig()
+	assert.NotNil(t, a)
+}
+
+func TestOpensearchSettingsInitConfig(t *testing.T) {
+	c := NewHaDeployConfig().InitOpenSearch()
+	a := c.InitConfig()
+	assert.NotNil(t, a)
+}
+
+func TestPostgresqlSettingsInitConfig(t *testing.T) {
+	c := NewHaDeployConfig().InitPostgresql()
+	a := c.InitConfig()
+	assert.NotNil(t, a)
+}
+
+func TestExistingInfraSettingsInitConfig(t *testing.T) {
+	c := NewHaDeployConfig().InitExistingInfra()
+	a := c.InitConfig()
+	assert.NotNil(t, a)
+}
+
+func TestExternalSettingsInitDatabase(t *testing.T) {
+	c := NewHaDeployConfig().InitExternal()
+	a := c.InitDatabase()
+	assert.NotNil(t, a)
+}
+
+func TestExternalDBSettingsInitPostgresql(t *testing.T) {
+	c := NewHaDeployConfig().InitExternal().InitDatabase()
+	a := c.InitPostgresql()
+	assert.NotNil(t, a)
+}
+
+func TestExternalDBSettingsInitOpenSearch(t *testing.T) {
+	c := NewHaDeployConfig().InitExternal().InitDatabase()
+	a := c.InitOpenSearch()
+	assert.NotNil(t, a)
+}
+
+func TestExternalOsSettingsInitOpenSearchAws(t *testing.T) {
+	c := NewHaDeployConfig().InitExternal().InitDatabase().InitOpenSearch()
+	a := c.InitOpenSearchAws()
+	assert.NotNil(t, a)
+}
+
+func TestConfigAutomateSettingsInitCertsByIP(t *testing.T) {
+	c := NewHaDeployConfig().InitAutomate().InitConfig()
+	a := c.InitCertsByIP()
+	assert.NotNil(t, a)
+}
+
+func TestConfigSettingsInitCertsByIP(t *testing.T) {
+	c := NewHaDeployConfig().InitPostgresql().InitConfig()
+	a := c.InitCertsByIP()
+	assert.NotNil(t, a)
+}
+
+func TestConfigChefServerSettingsInitCertsByIP(t *testing.T) {
+	c := NewHaDeployConfig().InitChefServer().InitConfig()
+	a := c.InitCertsByIP()
+	assert.NotNil(t, a)
+}
+
+func TestConfigOpensearchSettingsInitCertsByIP(t *testing.T) {
+	c := NewHaDeployConfig().InitOpenSearch().InitConfig()
+	a := c.InitCertsByIP()
+	assert.NotNil(t, a)
+}
diff --git a/lib/hardwarecal/hardwarecal_test.go b/lib/hardwarecal/hardwarecal_test.go
new file mode 100644
index 000000000..ba04b682b
--- /dev/null
+++ b/lib/hardwarecal/hardwarecal_test.go
@@ -0,0 +1,286 @@
+package hardwarecal
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestHardwareCal(t *testing.T) {
+	tests := []struct {
+		name   string
+		req    *HardwareCalReq
+		expRes *HardwareCalRes
+		expErr error
+	}{
+		{
+			name: "80K_2Comp_24client_24client_1rep_30days",
+			req: &HardwareCalReq{
+				NoOfNodes:                     80000,
+				FreqCountComplianceScanPerDay: 2,
+				FreqCountClientRunPerDay:      24,
+				FreqCountEventFeedPerDay:      24,
+				ComplianceReportSizeKB:        1024,
+				ClientRunReportSizeKB:         300,
+				EventFeedSizeKB:               100,
+				DataRetentionDays:             30,
+				NoOfReplicasInOpenSearch:      1,
+			},
+			expRes: &HardwareCalRes{
+				AutomateNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      4,
+					RamGB:         16,
+					StorageGB:     200,
+					Type:          "m5.xlarge",
+				},
+				ChefServerNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      4,
+					RamGB:         16,
+					StorageGB:     200,
+					Type:          "m5.xlarge",
+				},
+				PostgresqlNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      8,
+					RamGB:         32,
+					StorageGB:     1600,
+					Type:          "m5.2xlarge",
+				},
+				OpenSearchNode: &Node{
+					InstanceCount: 5,
+					CpuCount:      16,
+					RamGB:         64,
+					StorageGB:     13300,
+					Type:          "m5.4xlarge",
+				},
+			},
+			expErr: nil,
+		},
+		{
+			name: "100K_2Comp_24client_24client_1rep_30days",
+			req: &HardwareCalReq{
+				NoOfNodes:                     100000,
+				FreqCountComplianceScanPerDay: 2,
+				FreqCountClientRunPerDay:      24,
+				FreqCountEventFeedPerDay:      24,
+				ComplianceReportSizeKB:        1024,
+				ClientRunReportSizeKB:         300,
+				EventFeedSizeKB:               100,
+				DataRetentionDays:             30,
+				NoOfReplicasInOpenSearch:      1,
+			},
+			expRes: &HardwareCalRes{
+				AutomateNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      8,
+					RamGB:         32,
+					StorageGB:     200,
+					Type:          "m5.2xlarge",
+				},
+				ChefServerNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      8,
+					RamGB:         32,
+					StorageGB:     200,
+					Type:          "m5.2xlarge",
+				},
+				PostgresqlNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      16,
+					RamGB:         64,
+					StorageGB:     2000,
+					Type:          "m5.4xlarge",
+				},
+				OpenSearchNode: &Node{
+					InstanceCount: 7,
+					CpuCount:      16,
+					RamGB:         64,
+					StorageGB:     11900,
+					Type:          "m5.4xlarge",
+				},
+			},
+			expErr: nil,
+		},
+		{
+			name: "50K_2Comp_24client_24client_1rep_30days",
+			req: &HardwareCalReq{
+				NoOfNodes:                     50000,
+				FreqCountComplianceScanPerDay: 2,
+				FreqCountClientRunPerDay:      24,
+				FreqCountEventFeedPerDay:      24,
+				ComplianceReportSizeKB:        1024,
+				ClientRunReportSizeKB:         300,
+				EventFeedSizeKB:               100,
+				DataRetentionDays:             30,
+				NoOfReplicasInOpenSearch:      1,
+			},
+			expRes: &HardwareCalRes{
+				AutomateNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      4,
+					RamGB:         16,
+					StorageGB:     200,
+					Type:          "m5.xlarge",
+				},
+				ChefServerNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      4,
+					RamGB:         16,
+					StorageGB:     200,
+					Type:          "m5.xlarge",
+				},
+				PostgresqlNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      8,
+					RamGB:         32,
+					StorageGB:     1000,
+					Type:          "m5.2xlarge",
+				},
+				OpenSearchNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      16,
+					RamGB:         64,
+					StorageGB:     13900,
+					Type:          "m5.4xlarge",
+				},
+			},
+			expErr: nil,
+		},
+		{
+			name: "50K_2Comp_24client_24client_0rep_30days",
+			req: &HardwareCalReq{
+				NoOfNodes:                     50000,
+				FreqCountComplianceScanPerDay: 2,
+				FreqCountClientRunPerDay:      24,
+				FreqCountEventFeedPerDay:      24,
+				ComplianceReportSizeKB:        1024,
+				ClientRunReportSizeKB:         300,
+				EventFeedSizeKB:               100,
+				DataRetentionDays:             30,
+				NoOfReplicasInOpenSearch:      0,
+			},
+			expRes: &HardwareCalRes{
+				AutomateNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      4,
+					RamGB:         16,
+					StorageGB:     200,
+					Type:          "m5.xlarge",
+				},
+				ChefServerNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      4,
+					RamGB:         16,
+					StorageGB:     200,
+					Type:          "m5.xlarge",
+				},
+				PostgresqlNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      8,
+					RamGB:         32,
+					StorageGB:     1000,
+					Type:          "m5.2xlarge",
+				},
+				OpenSearchNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      8,
+					RamGB:         32,
+					StorageGB:     6900,
+					Type:          "m5.2xlarge",
+				},
+			},
+			expErr: nil,
+		},
+		{
+			name: "20K_2Comp_24client_24client_1rep_30days",
+			req: &HardwareCalReq{
+				NoOfNodes:                     20000,
+				FreqCountComplianceScanPerDay: 2,
+				FreqCountClientRunPerDay:      24,
+				FreqCountEventFeedPerDay:      24,
+				ComplianceReportSizeKB:        1024,
+				ClientRunReportSizeKB:         300,
+				EventFeedSizeKB:               100,
+				DataRetentionDays:             30,
+				NoOfReplicasInOpenSearch:      1,
+			},
+			expRes: &HardwareCalRes{
+				AutomateNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      2,
+					RamGB:         8,
+					StorageGB:     200,
+					Type:          "m5.large",
+				},
+				ChefServerNode: &Node{
+					InstanceCount: 2,
+					CpuCount:      2,
+					RamGB:         8,
+					StorageGB:     200,
+					Type:          "m5.large",
+				},
+				PostgresqlNode: &Node{
+					InstanceCount: 3,
+					CpuCount:      2,
+					RamGB:         8,
+					StorageGB:     400,
+					Type:          "m5.large",
+				},
+				OpenSearchNode: &Node{
+					InstanceCount: 5,
+					CpuCount:      4,
+					RamGB:         16,
+					StorageGB:     3300,
+					Type:          "m5.xlarge",
+				},
+			},
+			expErr: nil,
+		},
+		{
+			name: "200K_2Comp_24client_24client_1rep_30days",
+			req: &HardwareCalReq{
+				NoOfNodes:                     200000,
+				FreqCountComplianceScanPerDay: 2,
+				FreqCountClientRunPerDay:      24,
+				FreqCountEventFeedPerDay:      24,
+				ComplianceReportSizeKB:        1024,
+				ClientRunReportSizeKB:         300,
+				EventFeedSizeKB:               100,
+				DataRetentionDays:             30,
+				NoOfReplicasInOpenSearch:      1,
+			},
+			expRes: nil,
+			expErr: errors.New("opensearch node count needed: 15, please see if you can reduce the input requirements so the OpenSearch Nodes needed comes down to max 9 for 64gb ram"),
+		},
+	}
+	for _, tt := range tests {
+		res, err := tt.req.NewCal()
+
+		assert.Equal(t, tt.expErr, err)
+		if res != nil {
+			assert.Equal(t, tt.expRes.AutomateNode.CpuCount, res.AutomateNode.CpuCount, "Automate CPU Count")
+			assert.Equal(t, tt.expRes.AutomateNode.InstanceCount, res.AutomateNode.InstanceCount, "Automate Instance Count")
+			assert.Equal(t, tt.expRes.AutomateNode.StorageGB, res.AutomateNode.StorageGB, "Automate Storage")
+			assert.Equal(t, tt.expRes.AutomateNode.RamGB, res.AutomateNode.RamGB, "Automate RAM")
+			assert.Equal(t, tt.expRes.AutomateNode.Type, res.AutomateNode.Type, "Automate Type")
+			assert.Equal(t, tt.expRes.ChefServerNode.CpuCount, res.ChefServerNode.CpuCount, "Chef Server CPU Count")
+			assert.Equal(t, tt.expRes.ChefServerNode.InstanceCount, res.ChefServerNode.InstanceCount, "Chef Server Instance Count")
+			assert.Equal(t, tt.expRes.ChefServerNode.StorageGB, res.ChefServerNode.StorageGB, "Chef Server Storage")
+			assert.Equal(t, tt.expRes.ChefServerNode.RamGB, res.ChefServerNode.RamGB, "Chef Server RAM")
+			assert.Equal(t, tt.expRes.ChefServerNode.Type, res.ChefServerNode.Type, "Chef Server Type")
+			assert.Equal(t, tt.expRes.PostgresqlNode.CpuCount, res.PostgresqlNode.CpuCount, "PostgreSQL CPU Count")
+			assert.Equal(t, tt.expRes.PostgresqlNode.InstanceCount, res.PostgresqlNode.InstanceCount, "PostgreSQL Instance Count")
+			assert.Equal(t, tt.expRes.PostgresqlNode.StorageGB, res.PostgresqlNode.StorageGB, "PostgreSQL Storage")
+			assert.Equal(t, tt.expRes.PostgresqlNode.RamGB, res.PostgresqlNode.RamGB, "PostgreSQL RAM")
+			assert.Equal(t, tt.expRes.PostgresqlNode.Type, res.PostgresqlNode.Type, "PostgreSQL Type")
+			assert.Equal(t, tt.expRes.OpenSearchNode.CpuCount, res.OpenSearchNode.CpuCount, "OpenSearch CPU Count")
+			assert.Equal(t, tt.expRes.OpenSearchNode.InstanceCount, res.OpenSearchNode.InstanceCount, "OpenSearch Instance Count")
+			assert.Equal(t, tt.expRes.OpenSearchNode.StorageGB, res.OpenSearchNode.StorageGB, "OpenSearch Storage")
+			assert.Equal(t, tt.expRes.OpenSearchNode.RamGB, res.OpenSearchNode.RamGB, "OpenSearch RAM")
+			assert.Equal(t, tt.expRes.OpenSearchNode.Type, res.OpenSearchNode.Type, "OpenSearch Type")
+		}
+	}
+}
diff --git a/lib/hardwarecal/hardwarecalprompt_test.go b/lib/hardwarecal/hardwarecalprompt_test.go
new file mode 100644
index 000000000..3ae3b1e18
--- /dev/null
+++ b/lib/hardwarecal/hardwarecalprompt_test.go
@@ -0,0 +1,89 @@
+package hardwarecal
+
+import (
+	"bytes"
+	"errors"
+	"io"
+	"os"
+	"testing"
+
+	"github.com/chef/automate/components/automate-deployment/pkg/cli"
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/ioutils"
+	"github.com/chef/automate/lib/pmt"
+	"github.com/stretchr/testify/assert"
+)
+
+const esc = "\033["
+const moveDown = esc + "1B"
+const moveUp = esc + "1A"
+const selectKey = "\r"
+const clearLine = esc + "2K\n"
+
+func pad(siz int, buf *bytes.Buffer) {
+	pu := make([]byte, 4096-siz)
+	for i := 0; i < 4096-siz; i++ {
+		pu[i] = 97
+	}
+	buf.Write(pu)
+}
+
+func input(buf *bytes.Buffer, input string) {
+	buf.WriteString(input)
+	pad(len(input), buf)
+}
+
+func TestHardwareCalPromptRun(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, "\r")
+	input(b, "\r")
+	input(b, "\r")
+	input(b, "\r")
+	input(b, "\r")
+	input(b, "\r")
+	input(b, "\r")
+	input(b, "\r")
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == "test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := pmt.PromptFactory(in, out, mfsu)
+
+	cwIn := &bytes.Buffer{}
+	cwOut := &bytes.Buffer{}
+	cw := cli.NewWriter(cwOut, cwOut, cwIn)
+
+	hp := &HardwareCalPrompt{
+		Prompt: p,
+		cw:     cw,
+	}
+	err := hp.Run()
+
+	assert.NoError(t, err)
+	output := cwOut.String()
+	expetedOutput := `Hardware Requirements
++-------------------+-------+------+----------+--------------------+------------------+
+| INSTANCE          | COUNT | VCPU | RAM (GB) | STORAGE SPACE (GB) | AWS MACHINE TYPE |
++-------------------+-------+------+----------+--------------------+------------------+
+| Chef Automate     |     2 |    2 |        8 |                200 | m5.large         |
+| Chef Infra Server |     2 |    2 |        8 |                200 | m5.large         |
+| PostgreSQL        |     3 |    2 |        8 |                200 | m5.large         |
+| OpenSearch        |     3 |    2 |        8 |               1300 | m5.large         |
++-------------------+-------+------+----------+--------------------+------------------+
+`
+	assert.Equal(t, expetedOutput, output)
+}
diff --git a/lib/pmt/prompt_test.go b/lib/pmt/prompt_test.go
new file mode 100644
index 000000000..459f267f1
--- /dev/null
+++ b/lib/pmt/prompt_test.go
@@ -0,0 +1,478 @@
+package pmt
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"math"
+	"os"
+	"strconv"
+	"strings"
+	"testing"
+
+	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/ioutils"
+	"github.com/stretchr/testify/assert"
+)
+
+func pad(siz int, buf *bytes.Buffer) {
+	pu := make([]byte, 4096-siz)
+	for i := 0; i < 4096-siz; i++ {
+		pu[i] = 97
+	}
+	buf.Write(pu)
+}
+
+func input(buf *bytes.Buffer, input string) {
+	buf.WriteString(input)
+	pad(len(input), buf)
+}
+
+func TestConfirm(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+	choice, err := p.Confirm("Do you want to test", "yes", "no")
+	assert.NoError(t, err)
+	assert.Equal(t, false, choice)
+}
+
+func TestSelect(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, moveDown+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+	index, choice, err := p.Select("Do you want to test", "yes", "no")
+	assert.NoError(t, err)
+	assert.Equal(t, "no", choice)
+	assert.Equal(t, 1, index)
+}
+
+func TestSelectSearch(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	input(b, "may"+moveDown+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+	index, choice, err := p.SelectSearch("Do you want to test", "yes", "no", "may be no", "may be yes", "must be", "could be")
+	assert.NoError(t, err)
+	assert.Equal(t, "may be yes", choice)
+	assert.Equal(t, 3, index)
+}
+
+func TestInputInt64(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	var num int64 = math.MaxInt64
+	input(b, fmt.Sprint(num)+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+	choice, err := p.InputInt64("give int64 of your choice")
+	assert.NoError(t, err)
+	assert.Equal(t, num, choice)
+}
+
+func TestInputInt(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	var num int = math.MaxInt32
+	input(b, fmt.Sprint(num)+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+	choice, err := p.InputInt("give int of your choice")
+	assert.NoError(t, err)
+	assert.Equal(t, num, choice)
+}
+
+func TestInputIntDefault(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	defNum := 1000
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+	choice, err := p.InputIntDefault("give int of your choice", defNum)
+	assert.NoError(t, err)
+	assert.Equal(t, defNum, choice)
+}
+
+func TestInputIntDefaultValidation(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	defNum := 99
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	min := 10
+	max := 100
+	validationFunc := func(input string) (err error) {
+		val, err := strconv.Atoi(input)
+		if err != nil {
+			return errors.New(INVALID_INT)
+		}
+		if val < min || val > max {
+			return fmt.Errorf("number should be in range: %v <= N <= %v", min, max)
+		}
+		return nil
+	}
+	choice, err := p.InputIntDefaultValidation("give int of your choice", defNum, validationFunc)
+	assert.NoError(t, err)
+	assert.Equal(t, defNum, choice)
+}
+
+func TestInputIntRange(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	defNum := 99
+	input(b, fmt.Sprint(defNum)+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	min := 10
+	max := 100
+
+	choice, err := p.InputIntRange("give int of your choice", min, max)
+	assert.NoError(t, err)
+	assert.Equal(t, defNum, choice)
+}
+
+func TestInputIntDefaultRange(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	defNum := 99
+	input(b, fmt.Sprint(defNum)+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	min := 10
+	max := 100
+
+	choice, err := p.InputIntDefaultRange("give int of your choice", defNum, min, max)
+	assert.NoError(t, err)
+	assert.Equal(t, defNum, choice)
+}
+
+func TestInputFloat64(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	defNum := 99.9
+	input(b, fmt.Sprint(defNum)+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputFloat64("give float64 of your choice")
+	assert.NoError(t, err)
+	assert.Equal(t, defNum, choice)
+}
+
+func TestInputStringMinMax(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := "abcde"
+	input(b, valid+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputStringMinMax("give string of your choice", 5, 10)
+	assert.NoError(t, err)
+	assert.Equal(t, valid, choice)
+}
+
+func TestInputWordDefault(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := "abcde"
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputWordDefault("give string of your choice", valid)
+	assert.NoError(t, err)
+	assert.Equal(t, valid, choice)
+}
+
+func TestInputWord(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := "abcde"
+	input(b, valid+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputWord("give string of your choice")
+	assert.NoError(t, err)
+	assert.Equal(t, valid, choice)
+}
+
+func TestInputStringRegex(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := "abcdez"
+	input(b, valid+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputStringRegex("give string of your choice", "^a.*z$")
+	assert.NoError(t, err)
+	assert.Equal(t, valid, choice)
+}
+
+func TestInputString(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := "abcdez"
+	input(b, valid+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputString("give string of your choice")
+	assert.NoError(t, err)
+	assert.Equal(t, valid, choice)
+}
+
+func TestInputStringRequired(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := " d "
+	input(b, valid+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputStringRequired("give string of your choice")
+	assert.NoError(t, err)
+	assert.Equal(t, strings.TrimSpace(valid), choice)
+}
+
+func TestInputPassword(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := " d "
+	input(b, valid+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputPassword("give password of your choice")
+	assert.NoError(t, err)
+	assert.Equal(t, valid, choice)
+}
+
+func TestInputPasswordRegex(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := " d "
+	input(b, valid+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputPasswordRegex("give password of your choice", "^.{3,10}$")
+	assert.NoError(t, err)
+	assert.Equal(t, valid, choice)
+}
+
+func TestInputStringRegexDefault(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := " d "
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputStringRegexDefault("give string of your choice", "^.{3,10}$", valid)
+	assert.NoError(t, err)
+	assert.Equal(t, strings.TrimSpace(valid), choice)
+}
+
+func TestInputStringDefault(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	valid := " d "
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputStringDefault("give string of your choice", valid)
+	assert.NoError(t, err)
+	assert.Equal(t, strings.TrimSpace(valid), choice)
+}
+
+func TestInputExistingFilePathDefault(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	validFilePath := "test.pem"
+	validFileContent := "TEST"
+	input(b, "\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == validFilePath {
+				data = []byte(validFileContent)
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == validFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputExistingFilePathDefault("give file of your choice", validFilePath)
+
+	assert.NoError(t, err)
+	assert.Equal(t, validFilePath, choice)
+
+	choiceContent, err := mfsu.ReadFile(choice)
+	assert.NoError(t, err)
+	assert.Equal(t, validFileContent, string(choiceContent))
+}
+
+func TestInputExistingFilePath(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	validFilePath := "test.pem"
+	validFileContent := "TEST"
+	input(b, validFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == validFilePath {
+				data = []byte(validFileContent)
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == validFilePath {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputExistingFilePath("give file of your choice")
+
+	assert.NoError(t, err)
+	assert.Equal(t, validFilePath, choice)
+
+	choiceContent, err := mfsu.ReadFile(choice)
+	assert.NoError(t, err)
+	assert.Equal(t, validFileContent, string(choiceContent))
+}
+
+func TestInputExistingFilePathHomeDir(t *testing.T) {
+	b := bytes.NewBuffer([]byte(""))
+	in := io.NopCloser(b)
+	validFilePath := "~/test.pem"
+	validFileContent := "TEST"
+	input(b, validFilePath+"\r")
+
+	mfsu := &fileutils.MockFileSystemUtils{
+		ReadFileFunc: func(filepath string) (data []byte, err error) {
+			if filepath == validFilePath {
+				data = []byte(validFileContent)
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+		StatFunc: func(name string) (fi os.FileInfo, err error) {
+			if name == os.Getenv("HOME")+"/test.pem" {
+				fi = fileutils.MockFileInfo{IsDirFunc: func() bool { return false }}
+			} else {
+				err = errors.New("file not found")
+			}
+			return
+		},
+	}
+	outBuf := &bytes.Buffer{}
+	out := ioutils.NewNopWriteCloser(outBuf)
+	p := PromptFactory(in, out, mfsu)
+
+	choice, err := p.InputExistingFilePath("give file of your choice")
+
+	assert.NoError(t, err)
+	assert.Equal(t, validFilePath, choice)
+
+	choiceContent, err := mfsu.ReadFile(choice)
+	assert.NoError(t, err)
+	assert.Equal(t, validFileContent, string(choiceContent))
+}
diff --git a/lib/sshutils/sshutils_test.go b/lib/sshutils/sshutils_test.go
index ccb52cfca..cba3057a0 100644
--- a/lib/sshutils/sshutils_test.go
+++ b/lib/sshutils/sshutils_test.go
@@ -2,7 +2,9 @@ package sshutils_test
 
 import (
 	"errors"
+	"io/ioutil"
 	"net"
+	"path/filepath"
 
 	"testing"
 
@@ -652,7 +654,8 @@ func TestCheckKnownHosts(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			sshu.SshClient = tt.args.MockSshClient
-			_, gotError := sshu.CheckKnownHosts()
+			khdir := t.TempDir()
+			_, gotError := sshu.CheckKnownHosts(filepath.Join(khdir, sshutils.AUTOMATE_KNOWN_HOSTS))
 			assert.Equal(t, gotError, tt.wantedError)
 		})
 	}
@@ -672,7 +675,8 @@ func TestCreateKnownHosts(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
-			gotError := sshu.CreateKnownHosts()
+			khdir := t.TempDir()
+			gotError := sshu.CreateKnownHosts(filepath.Join(khdir, sshutils.AUTOMATE_KNOWN_HOSTS))
 			assert.Equal(t, gotError, tt.wantedError)
 		})
 	}
@@ -682,10 +686,12 @@ func TestAddHostKey(t *testing.T) {
 	log, _ := logger.NewLogger("text", "debug")
 	sshu := sshutils.NewSSHUtil(&sshutils.SshClient{}, log)
 	type args struct {
-		host          string
-		remote        *netAddressTest
-		pubkey        ssh.PublicKey
-		MockSshClient sshutils.ISshClient
+		host            string
+		remote          *netAddressTest
+		pubkey          ssh.PublicKey
+		MockSshClient   sshutils.ISshClient
+		existingContent string
+		expectedContent string
 	}
 	tests := []struct {
 		description string
@@ -693,19 +699,71 @@ func TestAddHostKey(t *testing.T) {
 		wantErr     error
 	}{
 		{
-			description: "If the Adding host key was successfully done",
+			description: "If the Adding host key was successfully done, empty known host file",
 			args: args{
 				host: nodeIp1,
 				remote: &netAddressTest{
 					Address: nodeIp1,
 				},
 				pubkey: &sshPublicKeyTest{
-					key:  "",
-					data: nil,
+					key:  "test-key",
+					data: []byte("test-data"),
 				},
 				MockSshClient: &sshutils.MockSshClient{
 					Normalizefunc: func(address string) string {
-						return ""
+						return nodeIp1
+					},
+					Newfunc: func(files ...string) (ssh.HostKeyCallback, error) {
+						return func(host string, remote net.Addr, pubkey ssh.PublicKey) error {
+							return nil
+						}, nil
+					},
+				},
+				existingContent: "",
+				expectedContent: nodeIp1 + " test-key dGVzdC1kYXRh",
+			},
+			wantErr: nil,
+		},
+		{
+			description: "If the Adding host key was successfully done, existing known host file",
+			args: args{
+				host: nodeIp1,
+				remote: &netAddressTest{
+					Address: nodeIp1,
+				},
+				pubkey: &sshPublicKeyTest{
+					key:  "test-key",
+					data: []byte("test-data"),
+				},
+				MockSshClient: &sshutils.MockSshClient{
+					Normalizefunc: func(address string) string {
+						return nodeIp1
+					},
+					Newfunc: func(files ...string) (ssh.HostKeyCallback, error) {
+						return func(host string, remote net.Addr, pubkey ssh.PublicKey) error {
+							return nil
+						}, nil
+					},
+				},
+				existingContent: nodeIp2 + " test-key dGVzdC1kYXRh",
+				expectedContent: nodeIp2 + " test-key dGVzdC1kYXRh\n" + nodeIp1 + " test-key dGVzdC1kYXRh",
+			},
+			wantErr: nil,
+		},
+		{
+			description: "If the Adding host key was successfully done, existing known host file with new line at end",
+			args: args{
+				host: nodeIp1,
+				remote: &netAddressTest{
+					Address: nodeIp1,
+				},
+				pubkey: &sshPublicKeyTest{
+					key:  "test-key",
+					data: []byte("test-data"),
+				},
+				MockSshClient: &sshutils.MockSshClient{
+					Normalizefunc: func(address string) string {
+						return nodeIp1
 					},
 					Newfunc: func(files ...string) (ssh.HostKeyCallback, error) {
 						return func(host string, remote net.Addr, pubkey ssh.PublicKey) error {
@@ -713,6 +771,8 @@ func TestAddHostKey(t *testing.T) {
 						}, nil
 					},
 				},
+				existingContent: nodeIp2 + " test-key dGVzdC1kYXRh\n",
+				expectedContent: nodeIp2 + " test-key dGVzdC1kYXRh\n" + nodeIp1 + " test-key dGVzdC1kYXRh",
 			},
 			wantErr: nil,
 		},
@@ -720,8 +780,18 @@ func TestAddHostKey(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			sshu.SshClient = tt.args.MockSshClient
-			gotError := sshu.AddHostKey(tt.args.host, tt.args.remote, tt.args.pubkey)
+			khdir := t.TempDir()
+			f, err := ioutil.TempFile(khdir, "")
+			assert.NoError(t, err)
+			content := []byte(tt.args.existingContent)
+			_, err = f.Write(content)
+			assert.NoError(t, err)
+			gotError := sshu.AddHostKey(tt.args.host, f.Name(), tt.args.remote, tt.args.pubkey)
 			assert.Equal(t, gotError, tt.wantErr)
+			content, err = ioutil.ReadFile(f.Name())
+			assert.NoError(t, err)
+			actualContent := string(content)
+			assert.Equal(t, tt.args.expectedContent, actualContent)
 		})
 	}
 }
diff --git a/lib/stringutils/strings_test.go b/lib/stringutils/strings_test.go
index cb5687a55..6ae82ec97 100644
--- a/lib/stringutils/strings_test.go
+++ b/lib/stringutils/strings_test.go
@@ -33,3 +33,23 @@ func TestTitleReplace(t *testing.T) {
 	assert.Equal(t, "Helloworld", stringutils.TitleReplace("helloworld", "-", " "))
 	assert.Equal(t, "Hello Chef", stringutils.TitleReplace("hello chef", "_", "-"))
 }
+
+func TestGetLastLine(t *testing.T) {
+	// Test case 1: Empty input string
+	input := ""
+	expected := ""
+	actual := stringutils.GetLastLine(input)
+	assert.Equal(t, actual, expected)
+
+	// Test case 2: Single line input
+	input = "Hello, World!"
+	expected = "Hello, World!"
+	actual = stringutils.GetLastLine(input)
+	assert.Equal(t, actual, expected)
+
+	// Test case 3: Multiple lines input
+	input = "Line 1\nLine 2\nLine 3\nLast line"
+	expected = "Last line"
+	actual = stringutils.GetLastLine(input)
+	assert.Equal(t, actual, expected)
+}