Repository: chef/automate
Pull request #8122 (2023-08-10 15:01:42)

Updating config-gen and error messages
### :nut_and_bolt: Description: What code changed, and why?

**IAM role:**
- When the IAM role is attached to the bastion, the user should able to use IAM role instead of IAM User credentials

- prompt to create snapshot credentials
 
**Config gen update**
- Update the regex error with the sample pattern in the text label
- gen prompt should show that the subnet id should be added when the user chooses to provide to subnets
- Showing sample values in Label wherever necessary



### :chains: Related Resources
Jira: https://chefio.atlassian.net/browse/CHEF-4734
https://chefio.atlassian.net/browse/CHEF-5511

### :+1: Definition of Done

### :athletic_shoe: How to Build and Test the Change

### :white_check_mark: Checklist

**All PRs** must tick these:

- [ ] I have read the [CONTRIBUTING document](https://github.com/chef/automate/blob/master/CONTRIBUTING.md).
- [ ] All commits signed-off for the [Developer Certification of Origin](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#developer-certification-of-origin-dco).

With occasional exceptions, all PRs **from Progress employees** must tick these:

- [ ] Is the code clear? *(complicated code or lots of comments--subdivide and use well-named methods, meaningful variable names, etc.)*
- [ ] Consistency checked? *(user notifications, user prompts, visual patterns, code patterns, variable names)*
- [ ] Repeated code blocks eliminated? *(adapt and reuse existing components, blocks, functions, etc.)*
- [ ] Spelling, grammar, typos checked? *(at a minimum use `make spell` in any component directory)*
- [ ] Code well-formatted? *(indents, line breaks, etc. improve rather than hinder readability)*

All PRs **from Progress employees** should tick these if appropriate:

- [ ] Tests added/updated? (all new code needs new tests)
- [ ] Docs added/updated? (all customer-facing changes)

*Please add a note next to any checkbox above if you are NOT ticking it.*

### :camera: Screenshots, if applicable
[Demo Video](https://progresssoftware.sharepoint.com/:v:/s/ChefCoreC/EaIOyGBjZ2hNrsvSZ1ef2dMBlULUM-Rj-hoyaz5JecLZ7w?e=WqutIZ)

![image](https://github.com/chef/automate/assets/54614142/4fb0b870-3069-4c1a-ad63-9a172128094e)

Error message:
![image](https://github.com/chef/automate/assets/54614142/70ba02f2-74b5-494b-b840-80dd1d82e957)

IAM role prompt:
<img width="910" alt="image" src="https://github.com/chef/automate/assets/54614142/2c39bfcd-48c3-496a-84f9-ccfb08cae3c9">

Prompt to create snapshot cred
<img width="489" alt="image" src="https://github.com/chef/automate/assets/54614142/df28c96f-2934-45a4-978c-24bd1e5bde10">

Use existing credentials:
<img width="889" alt="image" src="https://github.com/chef/automate/assets/54614142/906f020e-ac6b-4cd5-8a57-597b5247b9c5">



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


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 573509876be4646dd2898c2b2c0ef318ba038dac |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/chef-automate/deploys/64d4fbdcc9d6cb000858913a |
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=8122)

[![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=8122) 0.0% Security Hotspots Reviewed on New Code (is less than 100%)  

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


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

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

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


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

[![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=8122) 0.0% Security Hotspots Reviewed on New Code (is less than 100%)  

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


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

[![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=8122) 0.0% Security Hotspots Reviewed on New Code (is less than 100%)  

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


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

[![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=8122) [![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=8122) 0.0% Coverage on New Code (is less than 80%)  
[![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=8122) 0.0% Security Hotspots Reviewed on New Code (is less than 100%)  

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


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

[![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=8122) [![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=8122) 0.0% Coverage on New Code (is less than 80%)  
[![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=8122) 0.0% Security Hotspots Reviewed on New Code (is less than 100%)  

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


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

[![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=8122) 0.0% Security Hotspots Reviewed on New Code (is less than 100%)  

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


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

[![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=8122) 0.0% Security Hotspots Reviewed on New Code (is less than 100%)  

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



Patch:
diff --git a/.bldr.toml b/.bldr.toml
index 750d3e441..16fee3b96 100644
--- a/.bldr.toml
+++ b/.bldr.toml
@@ -793,8 +793,10 @@ paths = [
   "api/config/shared/*",
   "api/external/annotations/*",
   "api/external/iam/*",
+  "api/interservice/cereal/*",
   "api/interservice/license_control/*",
   "components/automate-grpc/*",
+  "lib/cereal/*",
   "lib/db/*",
   "lib/grpc/*",
   "lib/io/*",
diff --git a/VERSION b/VERSION
index a619b0699..56ee1fc4a 100644
--- a/VERSION
+++ b/VERSION
@@ -1,1 +1,1 @@
-4.9.40
\ No newline at end of file
+4.9.55
\ No newline at end of file
diff --git a/components/automate-backend-deployment/README.md b/components/automate-backend-deployment/README.md
index 6e1c988db..e2e452a4b 100644
--- a/components/automate-backend-deployment/README.md
+++ b/components/automate-backend-deployment/README.md
@@ -1,9 +1,9 @@
 # Automate::Backend::Deployment
 
+
 This provides the `automate-backend-deployment` package.
 
 This package will build a package using terraform/a2ha-terraform, inspecs, test, certs and Makefile.
 
 This is the heart of the a2ha because this component will set up a workspace for a2ha and all the a2ha command will get available after installing this package.
 
-
diff --git a/components/automate-cli/cmd/chef-automate/applications.go b/components/automate-cli/cmd/chef-automate/applications.go
index 3a80d19e3..fc77adf1d 100644
--- a/components/automate-cli/cmd/chef-automate/applications.go
+++ b/components/automate-cli/cmd/chef-automate/applications.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"io"
+	"math"
 	"os"
 	"os/exec"
 	"strings"
@@ -498,30 +499,36 @@ func (s *serviceSet) Load() error {
 //   - Print table rows as TSV for easier processing, even though the columns
 //     won't align all the time.
 func (s *serviceSet) PrintTSV() error {
-	svc := &applications.Service{}
-	if len(s.services) >= 1 {
-		svc = s.services[0]
+	maxIDLength, maxGroupLength, maxReleaseLength, maxFqdnLength, maxApplicationLength, maxEnvironmentLength, maxStatusLabelLength := 0, 0, 0, 0, 0, 0, 0
+	for _, t := range s.services {
+		maxIDLength = int(math.Max(float64(maxIDLength), float64(len(t.Id))))
+		maxGroupLength = int(math.Max(float64(maxGroupLength), float64(len(t.Group))))
+		maxReleaseLength = int(math.Max(float64(maxReleaseLength), float64(len(t.Release))))
+		maxFqdnLength = int(math.Max(float64(maxFqdnLength), float64(len(t.Fqdn))))
+		maxApplicationLength = int(math.Max(float64(maxApplicationLength), float64(len(t.Application))))
+		maxEnvironmentLength = int(math.Max(float64(maxEnvironmentLength), float64(len(t.Environment))))
+		maxStatusLabelLength = int(math.Max(float64(maxStatusLabelLength), float64(len(s.statusLabelFor(t)))))
 	}
 
 	// calculate a format string with fixed width, right padded strings for each
 	// of the columns
 	fmtString := fmt.Sprintf("%%-%ds\t%%-%ds\t%%-%ds\t%%-%ds\t%%-%ds\t%%-%ds\n",
-		len(svc.Id),
-		len(svc.Group),
-		len(svc.Release),
-		len(svc.Fqdn),
-		len(svc.Application)+len(svc.Environment)+1,
-		len(s.statusLabelFor(svc)),
+		maxIDLength,
+		maxGroupLength,
+		maxReleaseLength,
+		maxFqdnLength,
+		maxApplicationLength+maxEnvironmentLength+1,
+		maxStatusLabelLength,
 	)
 
 	fmt.Printf(fmtString, "id", "svc.group", "release", "FQDN", "app:env", "status")
 	for _, svc := range s.services {
-		fmt.Printf("%s\t%s\t%s\t%s\t%s:%s\t%s\n",
+		fmt.Printf(fmtString,
 			svc.Id,
 			svc.Group,
 			svc.Release,
 			svc.Fqdn,
-			svc.Application, svc.Environment,
+			svc.Application+":"+svc.Environment,
 			s.statusLabelFor(svc),
 		)
 	}
diff --git a/components/automate-cli/cmd/chef-automate/config.go b/components/automate-cli/cmd/chef-automate/config.go
index ec29d5f87..2752e4f89 100644
--- a/components/automate-cli/cmd/chef-automate/config.go
+++ b/components/automate-cli/cmd/chef-automate/config.go
@@ -559,7 +559,7 @@ func runPatchCommand(cmd *cobra.Command, args []string) error {
 		}
 
 		// keep chefserver fqdn same as automate fqdn in case of standalone automate
-		if !checkIfFileExist(automateHaPath) && cfg.Global.V1.ChefServer != nil && cfg.Global.V1.ChefServer.Fqdn != nil {
+		if !checkIfFileExist(automateHaPath) && cfg.GetGlobal().GetV1().GetChefServer() != nil && cfg.GetGlobal().GetV1().GetChefServer().GetFqdn() != nil {
 			res, err := client.GetAutomateConfig(configCmdFlags.timeout)
 			if err != nil {
 				return err
diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go
index 85e4f3c9f..dbc7aceb7 100644
--- a/components/automate-cli/cmd/chef-automate/verify.go
+++ b/components/automate-cli/cmd/chef-automate/verify.go
@@ -377,7 +377,8 @@ func (v *verifyCmdFlow) RunVerify(config string) error {
 	// Doing batch-check API call for remote nodes
 	resRemote, err := v.runVerifyServiceForRemote(*batchCheckConfig)
 	if err != nil {
-		return err
+		v.printResponse(batchCheckResults)
+		return status.New(status.VerifyChecksError, "Checks failed on Remote Nodes: "+err.Error())
 	}
 
 	if err := json.Unmarshal(resRemote, &batchCheckResultRemote); err != nil {
@@ -487,6 +488,14 @@ func (v *verifyCmdFlow) runVerifyServiceForRemote(batchCheckConfig models.Config
 		if err != nil {
 			return nil, err
 		}
+
+		notReachableIps, err := v.getHostIPsWithNoLatestCLI(hostIPs)
+		if err != nil {
+			return nil, err
+		}
+		if len(notReachableIps) > 0 {
+			return nil, errors.New("Nodes " + strings.Join(notReachableIps, ", ") + " are not reachable. This might be due to not enabling the 7799 port")
+		}
 	}
 
 	// Doing batch-check API call for remote nodes
@@ -626,6 +635,9 @@ func (v *verifyCmdFlow) getResultFromResponseBody(responseBody []byte) ([]byte,
 		return nil, fmt.Errorf("failed to unmarshal response body: %v", err)
 	}
 
+	if responseBodyStruct.Error != nil {
+		return nil, responseBodyStruct.Error
+	}
 	var resultBytes []byte
 	var err error
 	if v.prettyPrint {
diff --git a/components/automate-cli/pkg/verifyserver/constants/sshusercheck.go b/components/automate-cli/pkg/verifyserver/constants/sshusercheck.go
index 362929afe..4b0109569 100644
--- a/components/automate-cli/pkg/verifyserver/constants/sshusercheck.go
+++ b/components/automate-cli/pkg/verifyserver/constants/sshusercheck.go
@@ -9,7 +9,7 @@ const (
 	SUDO_PASSWORD_SUCCESS_MESSAGE               = "SSH user sudo password is valid for the node: "
 	SSH_USER_FAILURE_TITLE                      = "SSH user unaccessible"
 	SSH_USER_ERROR_MESSAGE                      = "SSH user is unaccessible for the node with IP: "
-	SSH_USER_RESOLUTION_MESSAGE                 = "Give SSH access to the user with the give key on the node: "
+	SSH_USER_RESOLUTION_MESSAGE                 = "Give SSH access to the user with the given key on the node: "
 	SUDO_PASSWORD_FAILURE_TITLE                 = "Sudo password invalid"
 	SUDO_PASSWORD_ERROR_MESSAGE                 = "SSH user sudo password is invalid for the node with IP: "
 	SUDO_PASSWORD_FAILURE_RESOLUTION_MESSAGE    = "Ensure you have provided the correct sudo password and the user has sudo access on the node: "
diff --git a/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn.go b/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn.go
index 7244abab2..19ea1593b 100644
--- a/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn.go
+++ b/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn.go
@@ -4,6 +4,7 @@ import (
 	"net"
 	"net/http"
 	"strings"
+	"time"
 
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
@@ -36,6 +37,6 @@ func (h *Handler) CheckFqdn(c *fiber.Ctx) error {
 		return fiber.NewError(http.StatusBadRequest, "node_type should be automate or chef-infra-server, Please provide node_type.")
 	}
 
-	res := h.FqdnService.CheckFqdnReachability(*req, constants.DEFAULT_HTTPS_PORT)
+	res := h.FqdnService.CheckFqdnReachability(*req, constants.DEFAULT_HTTPS_PORT, time.Minute*1)
 	return c.JSON(response.BuildSuccessResponse(res))
 }
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go
index 1bcaf1b5a..a20a3559a 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger.go
@@ -84,8 +84,8 @@ func (ss *SshUserAccessCheck) getSShUserAPIRequest(ip string, sshUser *models.SS
 	if err != nil {
 		return models.SShUserRequest{}, err
 	}
-	if permisssion > 400 {
-		return models.SShUserRequest{}, errors.New("Provide permission on the SSH key file as 400 (Read Only by Owner).")
+	if permisssion > 600 {
+		return models.SShUserRequest{}, errors.New("Provide permission on the SSH key file as 400 (Read Only by Owner) or 600 (Read and Write by Owner).")
 	}
 	ct, err := ss.fileUtils.ReadFile(sshUser.PrivateKey)
 	if err != nil {
diff --git a/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice.go b/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice.go
index 700c5d59b..bcadd9eea 100644
--- a/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice.go
+++ b/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice.go
@@ -19,7 +19,7 @@ import (
 )
 
 type IFqdnService interface {
-	CheckFqdnReachability(models.FqdnRequest, string) models.FqdnResponse
+	CheckFqdnReachability(models.FqdnRequest, string, time.Duration) models.FqdnResponse
 }
 
 type FqdnService struct {
@@ -111,35 +111,41 @@ func makeSet(reqNodes []string, isAfterDeployment bool) (map[string]int, error)
 }
 
 // makeConcurrentCalls function is making 50 concurrent calls for checking node reachability.
-func (fq *FqdnService) MakeConcurrentCalls(url string, client *http.Client, setNodes map[string]int) error {
+func (fq *FqdnService) MakeConcurrentCalls(url string, client *http.Client, setNodes map[string]int, duration time.Duration) error {
 	fq.log.Debug("Making Concurrent Calls...")
 	fqdnResultChan := make(chan string)
 	minNumberOfCalls := 5 * len(setNodes)
-	for i := 0; i < minNumberOfCalls; i++ {
-		go func(fqdnResultChan chan string) {
-			res, err := client.Get(url)
-			if err != nil {
-				fq.log.Error(err.Error())
-				fqdnResultChan <- constants.CHAN_RESULT_ERROR_MESSAGE
-				return
-			}
-			nodeIP := res.Header.Get(constants.SERVER_IP_HEADER_KEY)
-			fqdnResultChan <- nodeIP
-		}(fqdnResultChan)
-	}
-
-	for i := 0; i < minNumberOfCalls; i++ {
-		chanResult := <-fqdnResultChan
-		if chanResult == constants.CHAN_RESULT_ERROR_MESSAGE {
-			continue
+	timestamp := time.Now().Add(duration)
+	for time.Now().Before(timestamp) {
+		for i := 0; i < minNumberOfCalls; i++ {
+			go func(fqdnResultChan chan string) {
+				res, err := client.Get(url)
+				if err != nil {
+					fq.log.Error(err.Error())
+					fqdnResultChan <- constants.CHAN_RESULT_ERROR_MESSAGE
+					return
+				}
+				nodeIP := res.Header.Get(constants.SERVER_IP_HEADER_KEY)
+				fqdnResultChan <- nodeIP
+			}(fqdnResultChan)
 		}
-		delete(setNodes, chanResult)
-		//if setNodes becomes empty, that means we are able to reach all the nodes given in the request body.
-		if len(setNodes) == 0 {
-			fq.log.Debug("All nodes are reachable.")
-			return nil
+
+		for i := 0; i < minNumberOfCalls; i++ {
+			chanResult := <-fqdnResultChan
+			if chanResult == constants.CHAN_RESULT_ERROR_MESSAGE {
+				continue
+			}
+			delete(setNodes, chanResult)
+			//if setNodes becomes empty, that means we are able to reach all the nodes given in the request body.
+			if len(setNodes) == 0 {
+				fq.log.Debug("All nodes are reachable.")
+				return nil
+			}
 		}
+
+		time.Sleep(5 * time.Second)
 	}
+
 	return errors.New("nodes are not reachable")
 }
 
@@ -181,7 +187,7 @@ func (fq *FqdnService) fqdnReachable(fqdn, rootCert, nodeType string, isAfterDep
 }
 
 // nodeReachable function will check that our load balancer will correctly redirecting to all the nodes or not.
-func (fq *FqdnService) nodeReachable(fqdn, rootCert string, reqNodes []string, isAfterDeployment bool, port string) models.Checks {
+func (fq *FqdnService) nodeReachable(fqdn, rootCert string, reqNodes []string, isAfterDeployment bool, port string, duration time.Duration) models.Checks {
 	fq.log.Debug("Checking Node Reachability...")
 	setNodes, err := makeSet(reqNodes, isAfterDeployment)
 	if err != nil {
@@ -199,7 +205,7 @@ func (fq *FqdnService) nodeReachable(fqdn, rootCert string, reqNodes []string, i
 
 	client := fq.createClient(rootCert)
 
-	err = fq.MakeConcurrentCalls(url, client, setNodes)
+	err = fq.MakeConcurrentCalls(url, client, setNodes, duration)
 	if err != nil {
 		fq.log.Debug("All nodes are not reachable")
 		errorMessage := createErrorMessage(setNodes, reqNodes, isAfterDeployment)
@@ -246,7 +252,7 @@ func createCheck(title string, passed bool, successMsg, errorMsg, resolutionMsg
 	}
 }
 
-func (fq *FqdnService) CheckFqdnReachability(req models.FqdnRequest, port string) models.FqdnResponse {
+func (fq *FqdnService) CheckFqdnReachability(req models.FqdnRequest, port string, duration time.Duration) models.FqdnResponse {
 	var response = models.FqdnResponse{}
 	var check models.Checks
 
@@ -268,7 +274,7 @@ func (fq *FqdnService) CheckFqdnReachability(req models.FqdnRequest, port string
 	response.Checks = append(response.Checks, check)
 
 	if fqdnReachabilityCheck {
-		check = fq.nodeReachable(req.Fqdn, req.RootCert, req.Nodes, req.IsAfterDeployment, port)
+		check = fq.nodeReachable(req.Fqdn, req.RootCert, req.Nodes, req.IsAfterDeployment, port, duration)
 	} else {
 		check = createCheck(constants.NODE_TITLE, false, "", fmt.Sprintf(constants.NODE_ERROR_MESSAGE, req.Nodes), constants.NODE_RESOLUTION_MESSAGE)
 	}
diff --git a/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservicemock.go b/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservicemock.go
index 7d1752952..630867a31 100644
--- a/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservicemock.go
+++ b/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservicemock.go
@@ -1,11 +1,15 @@
 package fqdnservice
 
-import "github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
+import (
+	"time"
+
+	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
+)
 
 type MockFqdnService struct {
-	CheckFqdnReachabilityFunc func(models.FqdnRequest, string) models.FqdnResponse
+	CheckFqdnReachabilityFunc func(models.FqdnRequest, string, time.Duration) models.FqdnResponse
 }
 
-func (mfq *MockFqdnService) CheckFqdnReachability(req models.FqdnRequest, port string) models.FqdnResponse {
-	return mfq.CheckFqdnReachabilityFunc(req, port)
+func (mfq *MockFqdnService) CheckFqdnReachability(req models.FqdnRequest, port string, duration time.Duration) models.FqdnResponse {
+	return mfq.CheckFqdnReachabilityFunc(req, port, duration)
 }
diff --git a/components/automate-deployment/pkg/assets/binds.go b/components/automate-deployment/pkg/assets/binds.go
index 37308bbd5..1bac508bf 100644
--- a/components/automate-deployment/pkg/assets/binds.go
+++ b/components/automate-deployment/pkg/assets/binds.go
@@ -82,7 +82,7 @@ infra-proxy-service BINDING_MODE strict
 ingest-service REQUIRED authz-service automate-es-gateway automate-pg-gateway cereal-service config-mgmt-service event-feed-service nodemanager-service pg-sidecar-service
 ingest-service OPTIONAL es-sidecar-service
 ingest-service BINDING_MODE strict
-license-control-service REQUIRED automate-pg-gateway pg-sidecar-service
+license-control-service REQUIRED automate-pg-gateway cereal-service pg-sidecar-service
 license-control-service BINDING_MODE strict
 local-user-service REQUIRED authz-service automate-dex teams-service
 local-user-service BINDING_MODE strict
diff --git a/components/automate-deployment/pkg/services/internal/generated/gen.go b/components/automate-deployment/pkg/services/internal/generated/gen.go
index 68f102a5c..9c6ec50fb 100644
--- a/components/automate-deployment/pkg/services/internal/generated/gen.go
+++ b/components/automate-deployment/pkg/services/internal/generated/gen.go
@@ -529,6 +529,7 @@ var ProductMetadataJSON = `
       "type": "base",
       "services": [
         "chef/backup-gateway",
+        "chef/cereal-service",
         "chef/license-control-service",
         "chef/automate-load-balancer"
       ],
diff --git a/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md b/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md
index 230ecb6f1..80afd49b1 100644
--- a/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md
+++ b/components/docs-chef-io/content/automate/ha_aws_deploy_steps.md
@@ -25,9 +25,9 @@ Follow the steps below to deploy Chef Automate High Availability (HA) on AWS (Am
 - If you want to use Default VPC, we have to create public and private subnets, If subnets are unavailable. Please refer [this](https://docs.aws.amazon.com/vpc/latest/userguide/default-vpc.html)
 - We need three private and three public subnets in a vpc (1 subnet for each AZ). As of now, we support a dedicated subnet for each AZ.
 - We recommend creating a new vpc. And Bastion should be in the same VPC.
-- Get AWS credentials (`aws_access_key_id` and `aws_secret_access_key`) with privileges like: `AmazonS3FullAccess`, `AdministratorAccess`.
+- Attach IAM role to the Bastion with `AmazonS3FullAccess`, `AdministratorAccess` privileges or get AWS user credentials with the same privileges. Click [here](/automate/ha_iam_user/) to learn more about creating IAM Users.
 
-    Set these in `~/.aws/credentials` in Bastion Host:
+Set the AWS user credentials in `~/.aws/credentials` in Bastion Host:
 
     ```bash
     sudo su -
@@ -94,6 +94,15 @@ Run the following steps on Bastion Host Machine:
     chef-automate config gen config.toml
     "
     ```
+    or 
+
+    Generate the empty config and populate manually
+    ```bash
+    sudo -- sh -c "
+    chef-automate init-ha-config aws
+    "
+    ```
+    
     Click [here](/automate/ha_config_gen) to know more about generating config
 
 
@@ -111,6 +120,12 @@ Run the following steps on Bastion Host Machine:
     "
     ```
 
+{{< note >}}
+
+Once the provisioning is successful, **if you have added custom DNS to your configuration file (`fqdn`), make sure to map the load-balancer FQDN from the output of the previous command to your DNS from DNS Provider**
+
+{{< /note >}}
+
 #####  Config Verify
 1. After successful provision, run verify config command:
 
@@ -124,7 +139,7 @@ Run the following steps on Bastion Host Machine:
 
 ##### Steps to deploy
 
-1. Once the provisioning is successful, **if you have added custom DNS to your configuration file (`fqdn`), make sure to map the load-balancer FQDN from the output of a previous command to your DNS from DNS Provider**. After that, continue with the deployment process with the following.
+1. The following command will run the deployment.
 
     ```bash
     sudo -- sh -c "
@@ -238,7 +253,7 @@ Assuming 10+1 nodes (1 bastion, 2 for automate UI, 2 for Chef-server, 3 for Post
     instance_count = "3"
 [aws]
   [aws.config]
-    profile = "default"
+    profile = "default"  # This should be commented incase if IAM role is attached
     region = "us-east-2"
     aws_vpc_id = "vpc12318h"
     private_custom_subnets = ["subnet-e556d512", "subnet-e556d513", "subnet-e556d514"]
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 ca317237f..680f0def8 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
@@ -122,8 +122,8 @@ The first column in the table below represents the source of the connection. The
 
 |                 | Chef Automate        | Chef Infra Server    | PostgreSQL                                | OpenSearch                           | Bastion | Automate Load Balancer |
 | --------------- | -------------------- | -------------------- | ----------------------------------------- | ------------------------------------ | ------- | ------------- |
-| Chef Automate   |                      |                      | 7432                                      | 9200                                 |         |               |
-| Infra Server    |                      |                      | 7432                                      | 9200                                 |         | 443              |
+| Chef Automate   |                      |                      | 7432, 9631                                       | 9200, 9631                                  |         |               |
+| Infra Server    |                      |                      | 7432, 9631                                       | 9200, 9631                                  |         | 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            |
@@ -146,6 +146,7 @@ The first column in the table below represents the source of the connection. The
 | TCP      | 7432        | HAProxy, which redirects to PostgreSQL Leader             |
 | TCP      | 6432        | Re-elect PostgreSQL Leader if PostgreSQL leader is down   |
 | TCP      | 5432        | Allows PostgreSQL nodes to connect with each other        |
+| TCP/UDP  | 7799        | Allows bastion to connect with automate-verify service    |
 
 ## Certificates
 
@@ -188,8 +189,8 @@ The AWS deployment specific pre-requisites are as follows:
 
 ### Storage Space for Bastion
 
--   Operating System Root Volume (`/`) must be at least 40GB. Temporary space (`/var/tmp`) must be at least 5GB.
--   Separate Hab volume should be provisioned and mounted at `/hab` with at least 100GB free space.
+-   Operating System Root Volume (`/`) must be at least 40GB. Temporary space (`/var/tmp`) must be at least 10GB.
+-   Separate Hab volume should be provisioned and mounted at `/hab` with at least 200GB space.
 
 ### SSH User
 
diff --git a/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md b/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md
index f92ccb3e5..f486b47a0 100644
--- a/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md
+++ b/components/docs-chef-io/content/automate/ha_aws_managed_deploy_steps.md
@@ -29,11 +29,11 @@ Follow the steps below to deploy Chef Automate High Availability (HA) on AWS (Am
 - Set up AWS OpenSearch 1.3 in the same VPC where we have the basion and automate ha node to be created. Click [here](/automate/create_amazon_opensearch/) to know more.
 - For Backup with Managed Service, we have only one option: ' Amazon S3`.
 - For Backup and Restore with Managed Service. Click [here](/automate/managed_services/#enabling-opensearch-backup-restore) to know more.
-- Get AWS credentials (`aws_access_key_id` and `aws_secret_access_key`) with privileges like: `AmazonS3FullAccess`, `AdministratorAccess`. Click [here](/automate/ha_iam_user/) to learn more about creating IAM Users.
 - Preferred key type will be ed25519
 - Ensure your Linux has the `sysctl` utility available in all nodes.
+- Attach IAM role to the Bastion with `AmazonS3FullAccess`, `AdministratorAccess` privileges or get AWS user credentials with the same privileges. Click [here](/automate/ha_iam_user/) to learn more about creating IAM Users.
 
-Set the above prerequisites in `~/.aws/credentials` in Bastion Host, This step is not required in case of IAM role is configured on Bastion Host:
+Set the AWS user credentials in `~/.aws/credentials` in Bastion Host:
 
   ```bash
   sudo su -
@@ -99,7 +99,15 @@ Set the above prerequisites in `~/.aws/credentials` in Bastion Host, This step i
     "
     ```
     Click [here](/automate/ha_config_gen) to know more about generating config
-    
+
+    or
+
+    Generate the empty config and populate manually
+    ```bash
+    sudo -- sh -c "
+    chef-automate init-ha-config aws
+    "
+    ```
     {{< warning spaces=4 >}}
     {{% automate/char-warn %}}
     {{< /warning >}}
@@ -117,6 +125,12 @@ Set the above prerequisites in `~/.aws/credentials` in Bastion Host, This step i
     "
     ```
 
+{{< note >}}
+
+Once the provisioning is successful, **if you have added custom DNS to your configuration file (`fqdn`), make sure to map the load-balancer FQDN from the output of the previous command to your DNS from DNS Provider**
+
+{{< /note >}}
+
 #####  Config Verify
 1. After successful provision, run verify config command:
 
@@ -130,7 +144,7 @@ Set the above prerequisites in `~/.aws/credentials` in Bastion Host, This step i
 
 
 ##### Steps to deploy
-1. Once the provisioning is successful, **if you have added custom DNS to your configuration file (`fqdn`), make sure to map the load-balancer FQDN from the output of the previous command to your DNS from DNS Provider**. After that, continue with the deployment process with the following.
+1. The following command will run the deployment.
 
     ```bash
     sudo -- sh -c "
@@ -245,7 +259,7 @@ After successful deployment, proceed with following...
     instance_count = "0"
 [aws]
   [aws.config]
-    profile = "default"
+    profile = "default"   # This should be commented incase if IAM role is attached
     region = "us-east-2"
     aws_vpc_id = "vpc12318h"
     private_custom_subnets = ["subnet-e556d512", "subnet-e556d513", "subnet-e556d514"]
diff --git a/components/docs-chef-io/content/automate/ha_config_gen.md b/components/docs-chef-io/content/automate/ha_config_gen.md
index b412f1959..5571efd2c 100644
--- a/components/docs-chef-io/content/automate/ha_config_gen.md
+++ b/components/docs-chef-io/content/automate/ha_config_gen.md
@@ -20,7 +20,7 @@ gh_repo = "automate"
     chef-automate config gen config.toml
     "
     ```
-    
+  
 `config.toml` is the file where all the generated config will be saved at the end, you can choose to not provide this then the output will be shown to stdout.
 
 Refer the fields below to generate Chef Automate High Availability (HA) configuration using `chef-automate config gen` command
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 c8c94c0dd..0de87870c 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
@@ -114,8 +114,8 @@ The first column in the table below represents the source of the connection. The
 
 |               | Chef Automate  | Chef Infra Server | PostgreSQL                          | OpenSearch                           | Bastion | Automate Load Balancer |
 |---------------|----------------|-------------------|-------------------------------------|--------------------------------------|---------| ------------- |
-| Chef Automate |                |                   | 7432                                | 9200                                 |         |               |
-| Infra Server  |                |                   | 7432                                | 9200                                 |         | 443              |
+| Chef Automate |                |                   | 7432, 9631                                | 9200, 9631                                  |         |               |
+| Infra Server  |                |                   | 7432, 9631                                 | 9200, 9631                                  |         | 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            |
@@ -137,6 +137,7 @@ The first column in the table below represents the source of the connection. The
 | TCP      | 7432        | HAProxy, which redirects to PostgreSQL Leader            |
 | TCP      | 6432        | Re-elect PostgreSQL Leader if PostgreSQL leader is down  |
 | TCP      | 5432        | Allows PostgreSQL nodes to connect with each other       |
+| TCP/UDP  | 7799        | Allows bastion to connect with automate-verify service   |
 
 ## Certificates
 
@@ -167,8 +168,8 @@ The on-premises deployment specific pre-requisites are as follows:
 
 ### Storage Space
 
-- Operating System Root Volume (`/`) must be at least 40GB. Temporary space (`/var/tmp`) must be at least 5GB.
-- Separate Hab volume should be provisioned and mounted at `/hab` with at least 100GB free space for all nodes except OpenSearch.
+- Operating System Root Volume (`/`) must be at least 40GB. Temporary space (`/var/tmp`) must be at least 10GB.
+- Separate Hab volume should be provisioned and mounted at `/hab` with at least 200GB for all nodes except OpenSearch.
 - For OpenSearch nodes, /hab volume should be calculated based on the data retention policy, and use the [Performance Benchmarks](/automate/ha_performance_benchmarks) for estimation.
 
 ### SSH User
diff --git a/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md b/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md
index 5f05ea6c6..57c9a3137 100644
--- a/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md
+++ b/components/docs-chef-io/content/automate/ha_onprim_deployment_procedure.md
@@ -96,6 +96,15 @@ sudo sed -i 's/SELINUX=enforcing/SELINUX=permissive/g' /etc/selinux/config
     ```
     Click [here](/automate/ha_config_gen) to know more about generating config
 
+    or
+
+    Generate the empty config and populate manually
+    ```bash
+    sudo -- sh -c "
+    chef-automate init-ha-config existing_infra
+    "
+    ```
+
 #####  Config Verify
 1. We verify the above config using the below command :
 
@@ -209,6 +218,15 @@ See the steps [here](#steps-to-run-on-bastion-host-machine) to run on Bastion to
     ```
     Click [here](/automate/ha_config_gen) to know more about generating config
 
+    or
+
+    Generate the empty config and populate manually
+    ```bash
+    sudo -- sh -c "
+    chef-automate init-ha-config existing_infra
+    "
+    ```
+
 #####  Config Verify
 1. We verify the above config using the below command:
 
@@ -330,6 +348,15 @@ See the steps [here](#run-these-steps-on-bastion-host-machine) to run on Bastion
     ```
     Click [here](/automate/ha_config_gen) to know more about generating config
 
+    or
+
+    Generate the empty config and populate manually
+    ```bash
+    sudo -- sh -c "
+    chef-automate init-ha-config existing_infra
+    "
+    ```
+
 #####  Config Verify
 1. We verify the above config using the below command :
 
diff --git a/components/license-control-service/habitat/config/config.toml b/components/license-control-service/habitat/config/config.toml
index f2368d29b..19e4dbfa4 100644
--- a/components/license-control-service/habitat/config/config.toml
+++ b/components/license-control-service/habitat/config/config.toml
@@ -9,3 +9,10 @@ root_ca_path ="{{pkg.svc_config_path}}/root_ca.crt"
 opt_out_path = "{{pkg.svc_data_path}}/opt_out"
 url = "{{cfg.telemetry.url}}"
 opt_out = "{{cfg.telemetry.opt_out}}"
+
+[cereal]
+{{~#eachAlive bind.cereal-service.members as |cereal-service|}}
+{{~#if @last}}
+target = "127.0.0.1:{{cereal-service.cfg.port}}"
+{{~/if}}
+{{~/eachAlive}}
diff --git a/components/license-control-service/habitat/plan.sh b/components/license-control-service/habitat/plan.sh
index 9f74a79dc..1401db1ba 100644
--- a/components/license-control-service/habitat/plan.sh
+++ b/components/license-control-service/habitat/plan.sh
@@ -12,7 +12,10 @@ pkg_license=('Chef-MLSA')
 pkg_deps=(
   chef/mlsa
   chef/automate-platform-tools
+  chef/license-audit
 )
+pkg_svc_user=root
+pkg_svc_group=root
 pkg_exports=(
   [port]=service.port
   [host]=service.host
@@ -20,6 +23,7 @@ pkg_exports=(
 pkg_binds=(
   [automate-pg-gateway]="port"
   [pg-sidecar-service]="port"
+  [cereal-service]="port"
 )
 pkg_exposes=(port)
 pkg_scaffolding="${local_scaffolding_origin:-chef}/automate-scaffolding-go"
diff --git a/components/license-control-service/licenseaudit/licenseauditworkflow.go b/components/license-control-service/licenseaudit/licenseauditworkflow.go
new file mode 100644
index 000000000..3673222c4
--- /dev/null
+++ b/components/license-control-service/licenseaudit/licenseauditworkflow.go
@@ -0,0 +1,236 @@
+package licenseaudit
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/chef/automate/lib/cereal"
+	"github.com/chef/automate/lib/logger"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+	log "github.com/sirupsen/logrus"
+	"github.com/teambition/rrule-go"
+)
+
+var (
+	WorkflowName         = cereal.NewWorkflowName("license-audit")
+	LicenseAuditTaskName = cereal.NewTaskName("license-audit-task")
+	ScheduleName         = "license-audit"
+	Command              = "HAB_LICENSE=accept-no-persist hab pkg exec chef/license-audit license-audit report automate -s %s -e %s -o %s"
+	CleanCVSFilesCommand = "HAB_LICENSE=accept-no-persist hab pkg exec chef/license-audit license-audit report automate clean"
+	OutputFileName       = "license-audit-report"
+	CleanReportFiles     = "rm -rf %s.*"
+)
+
+func InitCerealManager(ctx context.Context, cerealManager *cereal.Manager, workerCount int) error {
+	log.Info("Successfully starting license-audit-workflow")
+	err := cerealManager.RegisterWorkflowExecutor(WorkflowName, &LicenseAuditWorkflow{})
+	if err != nil {
+		log.Errorf("Found error in Registering workflow for license-audit %v", err)
+		return err
+	}
+
+	log.Info("Successfully registered migration-workflow")
+	err = cerealManager.RegisterTaskExecutor(LicenseAuditTaskName, &LicenseAuditTask{ExecuteCommand: NewExecute(logger.NewLogrusStandardLogger()), Command: Command}, cereal.TaskExecutorOpts{Workers: workerCount})
+	if err != nil {
+		log.Errorf("Found error in RegisterTaskExecutor for license-audit %v", err)
+		return err
+	}
+
+	rule, err := createRuleForSchedule()
+	if err != nil {
+		return errors.Wrapf(err, "Unable to create rule for schedule")
+	}
+
+	return createOrUpdateWorkflowSchedule(ctx, cerealManager, ScheduleName, WorkflowName, rule)
+
+}
+
+func createRuleForSchedule() (*rrule.RRule, error) {
+	t := time.Now()
+	modifiedTime := time.Date(t.Year(), t.Month(), t.Day(), 4, 0, 0, 0, t.Location())
+
+	return rrule.NewRRule(rrule.ROption{
+		Freq:     rrule.DAILY,
+		Interval: 1,
+		Dtstart:  modifiedTime,
+	})
+
+}
+
+func createOrUpdateWorkflowSchedule(ctx context.Context, cerealManager *cereal.Manager, scheduleName string, auditWorkflowName cereal.WorkflowName, rule *rrule.RRule) error {
+	err := cerealManager.CreateWorkflowSchedule(ctx, scheduleName, auditWorkflowName, AuditWorkflowParameters{Retries: 3}, true, rule)
+
+	if err == nil {
+		return nil
+	}
+
+	if err == cereal.ErrWorkflowScheduleExists {
+		schedule, err := cerealManager.GetWorkflowScheduleByName(ctx, scheduleName, auditWorkflowName)
+		if err != nil {
+			return errors.Wrapf(err, "failed to get scheduled workflow %s from cereal manager", scheduleName)
+		}
+		scheduledRule, err := schedule.GetRRule()
+		if err != nil {
+			return errors.Wrapf(err, "unable to get rrule for scheduled workflow %s", scheduleName)
+		}
+		if scheduledRule != rule {
+			err = cerealManager.UpdateWorkflowScheduleByName(ctx, scheduleName, auditWorkflowName, cereal.UpdateRecurrence(rule))
+			if err != nil {
+				return errors.Wrapf(err, "unable to update recurrence rule for scheduled workflow %s", scheduleName)
+			}
+		}
+
+	} else {
+		return errors.Wrapf(err, "could not continue creating license-audit workflow schedule %s", scheduleName)
+	}
+	return nil
+}
+
+type LicenseAuditWorkflow struct {
+}
+
+type LicenseAuditTask struct {
+	Command        string
+	ExecuteCommand ExecuteCommand
+}
+
+type AuditWorkflowParameters struct {
+	Retries int
+}
+
+type AuditWorkflowPayload struct {
+	Retries int
+}
+
+func (s *LicenseAuditWorkflow) OnStart(w cereal.WorkflowInstance,
+	ev cereal.StartEvent) cereal.Decision {
+
+	logrus.Info("Starting the workflow for audit")
+
+	workflowParams, err := getWorkflowParametersAndEnqueTask(w)
+	if err != nil {
+		return w.Fail(err)
+	}
+
+	workflowPayload := &AuditWorkflowPayload{
+		Retries: workflowParams.Retries,
+	}
+
+	return w.Continue(workflowPayload)
+}
+
+func (s *LicenseAuditWorkflow) OnTaskComplete(w cereal.WorkflowInstance,
+	ev cereal.TaskCompleteEvent) cereal.Decision {
+
+	var payload AuditWorkflowPayload
+	if err := w.GetPayload(&payload); err != nil {
+		err = errors.Wrap(err, "failed to unmarshal license-audit payload in OnComplete")
+		log.WithError(err).Error()
+		return w.Fail(err)
+	}
+
+	log.Debugf("Entered license-audit  > OnTaskComplete with payload %+v", payload)
+
+	if err := ev.Result.Err(); err != nil {
+		//received error, if the retries are available enqueue the task
+		if payload.Retries > 0 {
+			logrus.Debugf("Retrying license audit")
+			_, err := getWorkflowParametersAndEnqueTask(w)
+			if err != nil {
+				return w.Fail(err)
+			}
+			payload.Retries--
+			return w.Continue(&payload)
+		}
+
+		err = errors.Wrap(err, "failed to run license-audit")
+		logrus.WithError(err).Error()
+		return w.Fail(err)
+	}
+
+	//get the results returned by task run
+	var jobResult AuditWorkflowParameters
+	err := ev.Result.Get(&jobResult)
+	if err != nil {
+		err = errors.Wrap(err, "failed to get the task run result in OnTaskComplete for license audit")
+		logrus.WithError(err).Error()
+		return w.Fail(err)
+	}
+
+	log.Debugf("Exiting license-audit > OnTaskComplete with payload %+v", payload)
+
+	return w.Complete()
+}
+
+func (s *LicenseAuditWorkflow) OnCancel(w cereal.WorkflowInstance, ev cereal.CancelEvent) cereal.Decision {
+	return w.Complete()
+}
+
+type AuditTaskParameters struct {
+}
+
+func (t *LicenseAuditTask) Run(ctx context.Context, task cereal.Task) (interface{}, error) {
+	log.Info("Executing the task For Run method")
+	var job AuditTaskParameters
+	if err := task.GetParameters(&job); err != nil {
+		err = errors.Wrap(err, "Unable to marshal Task Parameters for Audit task")
+		log.WithError(err).Error()
+		return nil, err
+	}
+
+	output, err := executeCommandforAudit(t.ExecuteCommand, getAppendedCommand(t.Command))
+	if err != nil {
+		log.Errorf("Failed to execute the command for audit with %v as error %s", err, output)
+		return nil, err
+	}
+
+	log.Infof("License analytics output received is %s", output)
+
+	//If no error in executing the command we can remove the files
+	deleteFilesGenerated(t.ExecuteCommand)
+
+	return &job, nil
+}
+
+// executeCommandforAudit executs the command
+func executeCommandforAudit(executeCommand ExecuteCommand, command string) (string, error) {
+	return executeCommand.Execute(command)
+}
+
+// getAppendedCommand gets the appended command with date
+func getAppendedCommand(commandToExecute string) string {
+	yesterdayDate := time.Now().AddDate(0, 0, -1).UTC().Format("2006-01-02")
+	return fmt.Sprintf(commandToExecute, yesterdayDate, yesterdayDate, OutputFileName)
+}
+
+func getWorkflowParametersAndEnqueTask(w cereal.WorkflowInstance) (AuditWorkflowParameters, error) {
+	workflowParams := AuditWorkflowParameters{}
+	err := w.GetParameters(&workflowParams)
+	if err != nil {
+		err = errors.Wrap(err, "failed to unmarshal license-audit workflow parameters")
+		logrus.WithError(err).Error()
+		return workflowParams, err
+	}
+
+	err = w.EnqueueTask(LicenseAuditTaskName, AuditTaskParameters{})
+	if err != nil {
+		err = errors.Wrap(err, "failed to enqueue the license-audit task")
+		logrus.WithError(err).Error()
+		return workflowParams, err
+	}
+
+	return workflowParams, nil
+}
+
+// deleteFilesGenerated deletes the files generated in license-audit
+func deleteFilesGenerated(executeCommand ExecuteCommand) {
+	commands := []string{CleanCVSFilesCommand, fmt.Sprintf(CleanReportFiles, OutputFileName)}
+	for _, command := range commands {
+		_, err := executeCommandforAudit(executeCommand, command)
+		if err != nil {
+			log.Errorf("Unable to delete the files with err %v", err)
+		}
+	}
+}
diff --git a/components/license-control-service/licenseaudit/licensecommand.go b/components/license-control-service/licenseaudit/licensecommand.go
new file mode 100644
index 000000000..01d52e3f0
--- /dev/null
+++ b/components/license-control-service/licenseaudit/licensecommand.go
@@ -0,0 +1,43 @@
+package licenseaudit
+
+import (
+	"bytes"
+	"fmt"
+	"os/exec"
+
+	"github.com/chef/automate/lib/logger"
+)
+
+type ExecuteCommand interface {
+	Execute(command string) (string, error)
+}
+
+type Execute struct {
+	log logger.Logger
+}
+
+func NewExecute(log logger.Logger) *Execute {
+	return &Execute{log: log}
+}
+
+func (e Execute) Execute(command string) (string, error) {
+
+	//Executing command
+	cmd := exec.Command("/bin/sh", "-c", command)
+
+	var outputBuffer, errorBuffer bytes.Buffer
+	cmd.Stdout = &outputBuffer
+	cmd.Stderr = &errorBuffer
+
+	//Executing the command
+	err := cmd.Run()
+	if err != nil {
+		return errorBuffer.String(), err
+	}
+
+	output := fmt.Sprintf("%s %s", outputBuffer.String(), errorBuffer.String())
+
+	e.log.Debugf("Got the output from the license command %s as %s", command, output)
+
+	return outputBuffer.String(), nil
+}
diff --git a/components/license-control-service/pkg/server/config.go b/components/license-control-service/pkg/server/config.go
index 5d525e48d..4232ce4a4 100644
--- a/components/license-control-service/pkg/server/config.go
+++ b/components/license-control-service/pkg/server/config.go
@@ -29,6 +29,7 @@ type Config struct {
 	OptOut           string          `mapstructure:"opt_out" toml:"opt_out"`
 	TelemetryEnabled bool
 	ServiceCerts     *certs.ServiceCerts
+	CerealConfig     CerealConfig `mapstructure:"cereal"`
 }
 
 // ListenAddress is the address where gRPC server will bind and listen
@@ -165,3 +166,7 @@ func removeOptOutFile(optOutPath string) error {
 
 	return err
 }
+
+type CerealConfig struct {
+	Target string `mapstructure:"target"`
+}
diff --git a/components/license-control-service/pkg/server/grpc.go b/components/license-control-service/pkg/server/grpc.go
index f8f0517f5..9d461ea72 100644
--- a/components/license-control-service/pkg/server/grpc.go
+++ b/components/license-control-service/pkg/server/grpc.go
@@ -4,14 +4,18 @@ import (
 	"context"
 	"net"
 
+	"github.com/chef/automate/components/license-control-service/licenseaudit"
+	grpccereal "github.com/chef/automate/lib/cereal/grpc"
 	"github.com/gofrs/uuid"
 	"github.com/pkg/errors"
+	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/reflection"
 
 	lc "github.com/chef/automate/api/interservice/license_control"
 	"github.com/chef/automate/components/license-control-service/pkg/keys"
 	"github.com/chef/automate/components/license-control-service/pkg/storage"
+	"github.com/chef/automate/lib/cereal"
 	"github.com/chef/automate/lib/grpc/health"
 	"github.com/chef/automate/lib/grpc/secureconn"
 	"github.com/chef/automate/lib/tracing"
@@ -21,12 +25,12 @@ import (
 func NewGRPC(ctx context.Context, config *Config) (*grpc.Server, error) {
 	// Setup our gRPC connection factory
 	connFactory := secureconn.NewFactory(*config.ServiceCerts)
-
 	// Register our API
 	grpcServer := connFactory.NewServer(tracing.GlobalServerInterceptor())
-
 	licenseParser := keys.NewLicenseParser(keys.BuiltinKeyData)
+
 	backend := storage.NewCurrentBackend(config.PGURL, config.MigrationsPath, config.LicenseTokenPath)
+
 	err := backend.Init(ctx, licenseParser)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to initialize storage backend")
@@ -42,6 +46,7 @@ func NewGRPC(ctx context.Context, config *Config) (*grpc.Server, error) {
 
 	// Register reflection service on gRPC server.
 	reflection.Register(grpcServer)
+
 	return grpcServer, nil
 }
 
@@ -58,10 +63,37 @@ func StartGRPC(ctx context.Context, config *Config) error {
 		return errors.Wrapf(err, "could no listen on %s", config.ListenAddress())
 	}
 
+	// Setup our gRPC connection factory
+	connFactory := secureconn.NewFactory(*config.ServiceCerts)
+
+	cerealConn, err := connFactory.Dial("cereal-service", config.CerealConfig.Target)
+	if err != nil {
+		return errors.Wrap(err, "error dialing cereal service")
+	}
+
+	cerealBackend := grpccereal.NewGrpcBackendFromConn("license-control-service", cerealConn)
+
+	// Set up a cereal (workflow) manager for managing nodemanager workflows.
+	cerealManager, err := cereal.NewManager(cerealBackend)
+	if err != nil {
+		return errors.Wrap(err, "could not create cereal manager")
+	}
+
+	err = licenseaudit.InitCerealManager(context.TODO(), cerealManager, 1)
+	if err != nil {
+		return errors.Wrap(err, "could not create cereal manager")
+	}
+
+	err = cerealManager.Start(context.Background())
+	if err != nil {
+		log.Errorf("Unable to start the cereal Manager for license control service %v", err)
+	}
+	defer cerealManager.Stop()
+
 	return g.Serve(listener)
 }
 
-//storeDeploymentID: Stores the deployment id in DB
+// storeDeploymentID: Stores the deployment id in DB
 func storeDeploymentID(backend storage.CurrentBackend) error {
 	id, err := uuid.NewV4()
 	if err != nil {
diff --git a/lib/config/genconfig/awshaprovisionconfig.go b/lib/config/genconfig/awshaprovisionconfig.go
index 4728f1f1d..6049a7101 100644
--- a/lib/config/genconfig/awshaprovisionconfig.go
+++ b/lib/config/genconfig/awshaprovisionconfig.go
@@ -1,54 +1,75 @@
 package genconfig
 
 import (
+	"encoding/json"
 	"fmt"
+	"net/http"
 	"strings"
 
 	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/httputils"
 	"github.com/chef/automate/lib/io/fileutils"
+	"github.com/chef/automate/lib/logger"
 	"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"
+	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_MACHINE_TYPE_REGEX_DEFAULT     = "m5.large"
+	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@:%._\\+~#?&//=]*)$"
+	ENDPOINT_URL_SAMPLE                = "http://bucket-endpoint.com or https://bucket-endpoint.com"
+	AUTOMATE_ADMIN_PASSWORD_REGEX      = "^.{8,35}$"
+	AWS_MACHINE_TYPE_REGEX_SAMPLE      = "m5.large, m5.xlarge"
+	IP_REGEX_SAMPLE                    = "172.31.0.0"
+	URL_OPTIONAL_PORT_REGEX_SAMPLE     = "myopensearch.com or 10.0.82.0:9200"
+	FQDN_REGEX_SAMPLE                  = "mydomain.chef.io"
+	URL_REQUIRED_PORT_REGEX_SAMPLE     = "myrds.chef.io:5432"
+	LINUX_USER_REGEX_SAMPLE            = "ubuntu or ec2-user"
+	BUCKET_NAME_REGEX_SAMPLE           = "automate-backup"
+	AWS_ACCESS_KEY_ID_REGEX_SAMPLE     = "A2PBA*************SQ"
+	AWS_ACCESS_KEY_SECRET_REGEX_SAMPLE = "aPBA*********************************EE"
+	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"
+	TOKEN_URLS                         = "http://169.254.169.254/latest/api/token"
+	METADATA_URLS                      = "http://169.254.169.254/latest/meta-data/iam/info"
 )
 
 type AwsHaProvisionConfig struct {
-	Prompt         pmt.Prompt `toml:"-"`
-	Config         *config.HaDeployConfig
-	HasCustomCerts bool                `toml:"-"`
-	FileUtils      fileutils.FileUtils `toml:"-"`
+	Prompt               pmt.Prompt `toml:"-"`
+	Config               *config.HaDeployConfig
+	HasCustomCerts       bool                `toml:"-"`
+	FileUtils            fileutils.FileUtils `toml:"-"`
+	CreateOSSnapShotCred bool
+	httpRequestClient    httputils.HTTPClient
 }
 
 func AwsHaProvisionConfigFactory(p pmt.Prompt) *AwsHaProvisionConfig {
+	log := logger.NewLogrusStandardLogger()
 	return &AwsHaProvisionConfig{
-		Prompt:    p,
-		Config:    &config.HaDeployConfig{},
-		FileUtils: fileutils.NewFileSystemUtils(),
+		Prompt:            p,
+		Config:            &config.HaDeployConfig{},
+		FileUtils:         fileutils.NewFileSystemUtils(),
+		httpRequestClient: httputils.NewClient(log),
 	}
 }
 
@@ -160,11 +181,18 @@ func (c *AwsHaProvisionConfig) PromptCidr() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptProfile() (err error) {
-	profile, err := c.Prompt.InputStringDefault("AWS Profile", "default")
+	useIAMRole, err := c.useIAMRole()
 	if err != nil {
 		return
 	}
-	c.Config.InitAws().InitConfigAwsSettings().Profile = profile
+
+	if !useIAMRole {
+		profile, err := c.Prompt.InputStringDefault("AWS Profile", "default")
+		if err != nil {
+			return err
+		}
+		c.Config.InitAws().InitConfigAwsSettings().Profile = profile
+	}
 	return
 }
 
@@ -188,7 +216,7 @@ func (c *AwsHaProvisionConfig) PromptVpcId() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) HasCidrBlockAddr() (isCiderBlock bool, err error) {
-	isCiderBlock, err = c.Prompt.Confirm("Do you want to use AWS CIDR Block", "yes", "no")
+	isCiderBlock, err = c.Prompt.Confirm("Create new subnets during provision (Requires CIDR block)", "yes", "no")
 	if err != nil {
 		return
 	}
@@ -201,8 +229,50 @@ func (c *AwsHaProvisionConfig) HasCidrBlockAddr() (isCiderBlock bool, err error)
 	return
 }
 
+func (c *AwsHaProvisionConfig) useIAMRole() (isIAMUserAvailable bool, err error) {
+
+	_, tokenResponseBody, err := c.httpRequestClient.MakeRequestWithHeaders(http.MethodPut, TOKEN_URLS, nil, "X-aws-ec2-metadata-token-ttl-seconds", "21600")
+	if err != nil {
+		return
+	}
+
+	token := string(tokenResponseBody)
+
+	resp, dataByte, err := c.httpRequestClient.MakeRequestWithHeaders(http.MethodGet, METADATA_URLS, nil, "X-aws-ec2-metadata-token", token)
+	if err != nil {
+		return
+	}
+
+	if resp.StatusCode != 200 {
+		isIAMUserAvailable = false
+		return
+	}
+
+	iamRole := getIAMRoleName(dataByte)
+	isIAMUserAvailable, err = c.Prompt.Confirm("Found an IAM role ("+iamRole+") attached to this machine. Do you want to continue with IAM role", "yes", "no")
+	return
+}
+
+func getIAMRoleName(respByte []byte) (IAMRoleName string) {
+
+	var result map[string]interface{}
+	err := json.Unmarshal(respByte, &result)
+	if err != nil {
+		fmt.Println("Error:", err)
+		return
+	}
+
+	instanceProfileArn, ok := result["InstanceProfileArn"].(string)
+	if !ok {
+		fmt.Println("InstanceProfileArn not found or not a string")
+		return
+	}
+	IAMRoleName = strings.Split(instanceProfileArn, "/")[1]
+	return
+}
+
 func (c *AwsHaProvisionConfig) PromptCidrBlockAddr() (err error) {
-	ciderBlockAddr, err := c.Prompt.InputStringRegex("AWS CIDR Block Address", IP_REGEX)
+	ciderBlockAddr, err := c.Prompt.InputStringRegex("AWS CIDR Block Address [Eg: 10.0.16.0]", IP_REGEX)
 	if err != nil {
 		return
 	}
@@ -211,19 +281,19 @@ func (c *AwsHaProvisionConfig) PromptCidrBlockAddr() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptPrivateSubnet() (err error) {
-	privateSubnet1, err := c.Prompt.InputStringRequired("AWS Private Subnet 1")
+	privateSubnet1, err := c.Prompt.InputStringRequired("AWS Private Subnet 1 [Eg: subnet-e556d512]")
 	if err != nil {
 		return
 	}
 	c.Config.InitAws().InitConfigAwsSettings().PrivateCustomSubnets = []string{privateSubnet1}
 
-	privateSubnet2, err := c.Prompt.InputStringRequired("AWS Private Subnet 2")
+	privateSubnet2, err := c.Prompt.InputStringRequired("AWS Private Subnet 2 [Eg: subnet-e556d514]")
 	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")
+	privateSubnet3, err := c.Prompt.InputStringRequired("AWS Private Subnet 3 [Eg: subnet-e556d513]")
 	if err != nil {
 		return
 	}
@@ -233,24 +303,24 @@ func (c *AwsHaProvisionConfig) PromptPrivateSubnet() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptPublicSubnet() (err error) {
-	hasPublicSubnets, err := c.Prompt.Confirm("Do you have Public Subnets", "yes", "no")
+	hasPublicSubnets, err := c.Prompt.Confirm("Create Public Subnets", "yes", "no")
 	if err != nil {
 		return
 	}
 	if hasPublicSubnets {
-		publicSubnet1, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 1:")
+		publicSubnet1, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 1 [Eg: subnet-e556d514]")
 		if err1 != nil {
 			return err1
 		}
 		c.Config.InitAws().InitConfigAwsSettings().PublicCustomSubnets = []string{publicSubnet1}
 
-		publicSubnet2, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 2:")
+		publicSubnet2, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 2 [Eg: subnet-e556d515]")
 		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:")
+		publicSubnet3, err1 := c.Prompt.InputStringRequired("AWS Public Subnet 3 [Eg: subnet-e556d515]")
 		if err1 != nil {
 			return err1
 		}
@@ -260,7 +330,7 @@ func (c *AwsHaProvisionConfig) PromptPublicSubnet() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptSshKeyPairName() (err error) {
-	sshKeyPairName, err := c.Prompt.InputStringRequired("AWS SSH Key Pair Name")
+	sshKeyPairName, err := c.Prompt.InputStringRequired("AWS SSH Key Pair Name [Eg: chef-ssh-key]")
 	if err != nil {
 		return
 	}
@@ -278,7 +348,7 @@ func (c *AwsHaProvisionConfig) PromptAmi() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptDeleteOnTermination() (err error) {
-	noDelOnTerm, err := c.Prompt.Confirm("Delete on termination should be", "off", "on")
+	noDelOnTerm, err := c.Prompt.Confirm("Delete root volume on termination", "off", "on")
 	if err != nil {
 		return
 	}
@@ -309,7 +379,7 @@ func (c *AwsHaProvisionConfig) PromptDatabases() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptIsAwsManaged() (isAwsManagedDb bool, err error) {
-	isAwsManagedDb, err = c.Prompt.Confirm("Do you want to use AWS Managed Databases", "yes", "no")
+	isAwsManagedDb, err = c.Prompt.Confirm("Use AWS Managed Databases", "yes", "no")
 	if err != nil {
 		return
 	}
@@ -365,7 +435,7 @@ func (c *AwsHaProvisionConfig) PromptAwsManagedPostgresql() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptPgUrl() (err error) {
-	pgUrl, err := c.Prompt.InputStringRegex("AWS Managed RDS PostgreSQL URL:<port>", URL_REQUIRED_PORT_REGEX)
+	pgUrl, err := c.Prompt.InputStringRegex("AWS Managed RDS PostgreSQL [Eg: pgdomain.com:5432]", URL_REQUIRED_PORT_REGEX)
 	if err != nil {
 		return
 	}
@@ -410,7 +480,7 @@ func (c *AwsHaProvisionConfig) PromptPgDbUserPassword() (err error) {
 }
 
 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")
+	awsDefaultCert, err := c.Prompt.Confirm("Use Default AWS Cert to connect with AWS Managed RDS PosgreSQL URL", "yes", "no")
 	if err != nil {
 		return
 	}
@@ -457,6 +527,11 @@ func (c *AwsHaProvisionConfig) PromptAwsManagedOpenSearch() (err error) {
 		return
 	}
 
+	err = c.CreateOSSnapShotCredentials()
+	if err != nil {
+		return
+	}
+
 	err = c.PromptOsSnapshotRoleArn()
 	if err != nil {
 		return
@@ -476,7 +551,7 @@ func (c *AwsHaProvisionConfig) PromptAwsManagedOpenSearch() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptOsDomainName() (err error) {
-	osDomainName, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Domain Name")
+	osDomainName, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Domain Name [Eg: chef-opensearch]")
 	if err != nil {
 		return
 	}
@@ -485,7 +560,7 @@ func (c *AwsHaProvisionConfig) PromptOsDomainName() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptOsDomainUrl() (err error) {
-	osDomainUrl, err := c.Prompt.InputStringRegex("AWS Managed OpenSearch Domain URL", URL_OPTIONAL_PORT_REGEX)
+	osDomainUrl, err := c.Prompt.InputStringRegex("AWS Managed OpenSearch Domain URL [Eg: chef-opensearch.myosdomain.com]", URL_OPTIONAL_PORT_REGEX)
 	if err != nil {
 		return
 	}
@@ -494,7 +569,7 @@ func (c *AwsHaProvisionConfig) PromptOsDomainUrl() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptOsUserName() (err error) {
-	user, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch User Name")
+	user, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch User Name [Eg: admin]")
 	if err != nil {
 		return
 	}
@@ -512,7 +587,7 @@ func (c *AwsHaProvisionConfig) PromptOsUserPassword() (err error) {
 }
 
 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")
+	awsDefaultCert, err := c.Prompt.Confirm("Use Default AWS Cert to connect with AWS Managed OpenSearch Domain URL", "yes", "no")
 	if err != nil {
 		return
 	}
@@ -533,30 +608,51 @@ func (c *AwsHaProvisionConfig) PromptOsCert() (err error) {
 	return
 }
 
-func (c *AwsHaProvisionConfig) PromptOsSnapshotRoleArn() (err error) {
-	arn, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Snapshot Role ARN")
+func (c *AwsHaProvisionConfig) CreateOSSnapShotCredentials() (err error) {
+	CreateOSSnapShotCred, err := c.Prompt.Confirm("Create new snapshot role ARN and user credential", "yes", "no")
 	if err != nil {
 		return
 	}
-	c.Config.InitAws().InitConfigAwsSettings().AwsOsSnapshotRoleArn = arn
+	c.CreateOSSnapShotCred = CreateOSSnapShotCred
+	return
+}
+
+func (c *AwsHaProvisionConfig) PromptOsSnapshotRoleArn() (err error) {
+	if c.CreateOSSnapShotCred {
+		c.Config.InitAws().InitConfigAwsSettings().AwsOsSnapshotRoleArn = ""
+	} else {
+		arn, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Snapshot Role ARN")
+		if err != nil {
+			return err
+		}
+		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
+	if c.CreateOSSnapShotCred {
+		c.Config.InitAws().InitConfigAwsSettings().OsSnapshotUserAccessKeyID = ""
+	} else {
+		keyId, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Snapshot User Access Key ID")
+		if err != nil {
+			return err
+		}
+		c.Config.InitAws().InitConfigAwsSettings().OsSnapshotUserAccessKeyID = keyId
 	}
-	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
+	if c.CreateOSSnapShotCred {
+		c.Config.InitAws().InitConfigAwsSettings().OsSnapshotUserAccessKeySecret = ""
+	} else {
+		secret, err := c.Prompt.InputStringRequired("AWS Managed OpenSearch Snapshot User Access Key Secret")
+		if err != nil {
+			return err
+		}
+		c.Config.InitAws().InitConfigAwsSettings().OsSnapshotUserAccessKeySecret = secret
 	}
-	c.Config.InitAws().InitConfigAwsSettings().OsSnapshotUserAccessKeySecret = secret
 	return
 }
 
@@ -729,7 +825,7 @@ func (c *AwsHaProvisionConfig) DefaultAutomateConfigValues() {
 }
 
 func (c *AwsHaProvisionConfig) PromptAutomateFqdn() (err error) {
-	automateFqdn, err := c.Prompt.InputStringRegex("Automate FQDN", FQDN_REGEX)
+	automateFqdn, err := c.Prompt.InputStringRegex("Automate FQDN [Eg: my-automate-domain.com]", FQDN_REGEX)
 	if err != nil {
 		return
 	}
@@ -739,7 +835,7 @@ func (c *AwsHaProvisionConfig) PromptAutomateFqdn() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptAutomateFqdnRootCa() (err error) {
-	automateFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Automate FQDN Root CA File Path")
+	automateFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Automate FQDN Root CA File Path [Eg: /home/ubuntu/fqdn1-root-ca.pem]")
 	if err != nil {
 		return
 	}
@@ -759,7 +855,7 @@ func (c *AwsHaProvisionConfig) PromptAutomateFqdnRootCa() (err error) {
 
 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")
+		customCerts, err = c.Prompt.Confirm("Use custom certs for "+nodeType+" Nodes", "yes", "no")
 		if err != nil {
 			return
 		}
@@ -828,7 +924,7 @@ func (c *AwsHaProvisionConfig) PromptAutomateAdminPassword() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptAutomateInstanceType() (err error) {
-	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for Automate", AWS_MACHINE_TYPE_REGEX, "m5.large")
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for Automate [min. reg.: m5.large]", AWS_MACHINE_TYPE_REGEX, "m5.large")
 	if err != nil {
 		return
 	}
@@ -837,7 +933,7 @@ func (c *AwsHaProvisionConfig) PromptAutomateInstanceType() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptChefInfraServerFqdn() (err error) {
-	chefInfraServerFqdn, err := c.Prompt.InputStringRegex("Chef Infra Server FQDN", FQDN_REGEX)
+	chefInfraServerFqdn, err := c.Prompt.InputStringRegex("Chef Infra Server FQDN [Eg: my-infraserver-domain.com]", FQDN_REGEX)
 	if err != nil {
 		return
 	}
@@ -847,7 +943,7 @@ func (c *AwsHaProvisionConfig) PromptChefInfraServerFqdn() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptChefInfraServerFqdnRootCa() (err error) {
-	chefInfraServerFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Chef Infra Server FQDN Root CA File Path")
+	chefInfraServerFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Chef Infra Server FQDN Root CA File Path [Eg: /home/ubuntu/fqdn2-root-ca.pem]")
 	if err != nil {
 		return
 	}
@@ -899,7 +995,7 @@ func (c *AwsHaProvisionConfig) PromptChefInfraServerNodes() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptChefInfraServerInstanceType() (err error) {
-	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for Chef Infra Server", AWS_MACHINE_TYPE_REGEX, "m5.large")
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for Chef Infra Server [min. reg.: m5.large]", AWS_MACHINE_TYPE_REGEX, AWS_MACHINE_TYPE_REGEX_DEFAULT)
 	if err != nil {
 		return
 	}
@@ -908,7 +1004,7 @@ func (c *AwsHaProvisionConfig) PromptChefInfraServerInstanceType() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptPostgresqlInstanceType() (err error) {
-	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for PostgreSQL", AWS_MACHINE_TYPE_REGEX, "m5.large")
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for PostgreSQL [min. reg.: m5.large]", AWS_MACHINE_TYPE_REGEX, AWS_MACHINE_TYPE_REGEX_DEFAULT)
 	if err != nil {
 		return
 	}
@@ -917,7 +1013,7 @@ func (c *AwsHaProvisionConfig) PromptPostgresqlInstanceType() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptOpenSearchInstanceType() (err error) {
-	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for OpenSearch", AWS_MACHINE_TYPE_REGEX, "m5.large")
+	instanceType, err := c.Prompt.InputStringRegexDefault("AWS Instance type for OpenSearch [min. reg.: m5.large]", AWS_MACHINE_TYPE_REGEX, AWS_MACHINE_TYPE_REGEX_DEFAULT)
 	if err != nil {
 		return
 	}
@@ -1118,7 +1214,7 @@ func (c *AwsHaProvisionConfig) PromptOpenSearchVolType() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptAutomateLoadBalancerCertArn() (err error) {
-	certArn, err := c.Prompt.InputStringRequired("AWS Load Balancer Cert ARN for Automate")
+	certArn, err := c.Prompt.InputStringRequired("AWS Load Balancer Cert ARN for Automate [Eg: arn:aws:acm:eu-west-1:*******:certificate/6b***]")
 	if err != nil {
 		return
 	}
@@ -1127,7 +1223,7 @@ func (c *AwsHaProvisionConfig) PromptAutomateLoadBalancerCertArn() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptChefInfrServerLoadBalancerCertArn() (err error) {
-	certArn, err := c.Prompt.InputStringRequired("AWS Load Balancer Cert ARN for Chef Infra Server")
+	certArn, err := c.Prompt.InputStringRequired("AWS Load Balancer Cert ARN for Chef Infra Server [Eg: arn:aws:acm:eu-west-1:*******:certificate/6b***]")
 	if err != nil {
 		return
 	}
@@ -1232,7 +1328,7 @@ func (c *AwsHaProvisionConfig) PromptSshKey() (err error) {
 }
 
 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")
+	noCustomCerts, err := c.Prompt.Confirm("Use custom certs for one or all service like Automate, Chef Infra Server, PostgreSQL, OpenSearch", "no", "yes")
 	if err != nil {
 		return
 	}
@@ -1245,13 +1341,13 @@ func (c *AwsHaProvisionConfig) PromptCustomCerts() (err error) {
 }
 
 func (c *AwsHaProvisionConfig) PromptBackup() (err error) {
-	isBackupNeeded, err := c.Prompt.Confirm("Backup need to be configured during deployment", "yes", "no")
+	isBackupNeeded, err := c.Prompt.Confirm("Configure backup 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")
+			_, backupOption, err1 := c.Prompt.Select("Backup type", "AWS S3", "EFS")
 			if err1 != nil {
 				return err1
 			}
@@ -1286,4 +1382,4 @@ func (c *AwsHaProvisionConfig) SetDefaultValuesForDBNodes() {
 	c.Config.InitPostgresql().InitConfig()
 	c.Config.Postgresql.Config.InstanceCount = "0"
 	c.Config.Postgresql.Config.EnableCustomCerts = false
-}
\ No newline at end of file
+}
diff --git a/lib/config/genconfig/hadeployconfig.go b/lib/config/genconfig/hadeployconfig.go
index 5bdcb7fab..7742b4587 100644
--- a/lib/config/genconfig/hadeployconfig.go
+++ b/lib/config/genconfig/hadeployconfig.go
@@ -94,7 +94,7 @@ func (c *HaDeployConfigGen) Prompts() (err error) {
 }
 
 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")
+	noCustomCerts, err := c.Prompt.Confirm("Use custom certs for one or all service like Automate, Chef Infra Server, PostgreSQL, OpenSearch", "no", "yes")
 	if err != nil {
 		return
 	}
@@ -117,7 +117,7 @@ func (c *HaDeployConfigGen) SetDefaultValuesForDBNodes() {
 }
 
 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")
+	hasExternalDb, err = c.Prompt.Confirm("Use External Databases for PostgresQL and OpenSearch", "yes", "no")
 	if err != nil {
 		return
 	}
@@ -143,7 +143,7 @@ func (c *HaDeployConfigGen) PromptExternalDb() (hasExternalDb bool, err error) {
 }
 
 func (c *HaDeployConfigGen) PromptExternalDbType() (err error) {
-	isAWSManaged, err := c.Prompt.Confirm("Type of External DB you will use", "AWS Managed", "Self Managed")
+	isAWSManaged, err := c.Prompt.Confirm("Type of External DB", "AWS Managed", "Self Managed")
 	if err != nil {
 		return
 	}
@@ -256,7 +256,7 @@ func (c *HaDeployConfigGen) GetExternalOsType() (dbType string) {
 }
 
 func (c *HaDeployConfigGen) PromptExternalPostgresqlInstanceUrl() (err error) {
-	pgUrl, err := c.Prompt.InputStringRegex(c.GetExternalPgType()+" URL:<port>", URL_REQUIRED_PORT_REGEX)
+	pgUrl, err := c.Prompt.InputStringRegex(c.GetExternalPgType()+" [Eg: pgdomain.com:5432]", URL_REQUIRED_PORT_REGEX)
 	if err != nil {
 		return
 	}
@@ -308,7 +308,7 @@ func (c *HaDeployConfigGen) PromptExternalPostgresqlDbUserPassword() (err error)
 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")
+		awsDefaultCert, err = c.Prompt.Confirm("Use Default AWS Cert to connect with AWS Managed RDS PostgreSQL URL", "yes", "no")
 		if err != nil {
 			return
 		}
@@ -336,7 +336,7 @@ func (c *HaDeployConfigGen) PromptExternalPostgresqlRootCert() (err error) {
 }
 
 func (c *HaDeployConfigGen) PromptExternalOpenSearchDomainName() (err error) {
-	domainName, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Domain Name")
+	domainName, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Domain Name [Eg: chef-opensearch]")
 	if err != nil {
 		return
 	}
@@ -346,7 +346,7 @@ func (c *HaDeployConfigGen) PromptExternalOpenSearchDomainName() (err error) {
 }
 
 func (c *HaDeployConfigGen) PromptExternalOpenSearchDomainUrl() (err error) {
-	domainUrl, err := c.Prompt.InputStringRegex(c.GetExternalOsType()+" Domain URL", URL_OPTIONAL_PORT_REGEX)
+	domainUrl, err := c.Prompt.InputStringRegex(c.GetExternalOsType()+" Domain URL [Eg: chef-opensearch.myosdomain.com]", URL_OPTIONAL_PORT_REGEX)
 	if err != nil {
 		return
 	}
@@ -356,7 +356,7 @@ func (c *HaDeployConfigGen) PromptExternalOpenSearchDomainUrl() (err error) {
 }
 
 func (c *HaDeployConfigGen) PromptExternalOpenSearchUser() (err error) {
-	user, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Username")
+	user, err := c.Prompt.InputStringRequired(c.GetExternalOsType() + " Username [Eg: admin]")
 	if err != nil {
 		return
 	}
@@ -378,7 +378,7 @@ func (c *HaDeployConfigGen) PromptExternalOpenSearchPassword() (err error) {
 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")
+		awsDefaultCert, err = c.Prompt.Confirm("Use Default AWS Cert to connect with AWS Managed OpenSearch Domain URL", "yes", "no")
 		if err != nil {
 			return
 		}
@@ -778,13 +778,13 @@ func (c *HaDeployConfigGen) PromptNodeIp(msg string) (ip string, err error) {
 
 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")
+		customCerts, err = c.Prompt.Confirm("Use 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")
+			commonCerts, err1 := c.Prompt.Confirm("Use same Certs for all "+nodeType+" Node", "yes", "no")
 			if err1 != nil {
 				return commonCerts, customCertsPerNode, err1
 			}
@@ -892,7 +892,7 @@ func (c *HaDeployConfigGen) PromptAutomateCerts(hasCustomCerts, hasCustomCertsPe
 }
 
 func (c *HaDeployConfigGen) PromptChefInfraServerFqdn() (err error) {
-	chefInfraServerFqdn, err := c.Prompt.InputStringRegex("Chef Infra Server FQDN", FQDN_REGEX)
+	chefInfraServerFqdn, err := c.Prompt.InputStringRegex("Chef Infra Server FQDN [Eg: my-infraserver-domain.com]", FQDN_REGEX)
 	if err != nil {
 		return
 	}
@@ -902,7 +902,7 @@ func (c *HaDeployConfigGen) PromptChefInfraServerFqdn() (err error) {
 }
 
 func (c *HaDeployConfigGen) PromptChefInfraServerFqdnRootCa() (err error) {
-	chefInfraServerFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Chef Infra Server FQDN Root CA File Path")
+	chefInfraServerFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Chef Infra Server FQDN Root CA File Path [Eg: /home/ubuntu/fqdn2-root-ca.pem]")
 	if err != nil {
 		return
 	}
@@ -921,7 +921,7 @@ func (c *HaDeployConfigGen) PromptChefInfraServerFqdnRootCa() (err error) {
 }
 
 func (c *HaDeployConfigGen) PromptAutomateFqdn() (err error) {
-	automateFqdn, err := c.Prompt.InputStringRegex("Automate FQDN", FQDN_REGEX)
+	automateFqdn, err := c.Prompt.InputStringRegex("Automate FQDN [Eg: my-automate-domain.com]", FQDN_REGEX)
 	if err != nil {
 		return
 	}
@@ -931,7 +931,7 @@ func (c *HaDeployConfigGen) PromptAutomateFqdn() (err error) {
 }
 
 func (c *HaDeployConfigGen) PromptAutomateFqdnRootCa() (err error) {
-	automateFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Automate FQDN Root CA File Path")
+	automateFqdnRootCaFilePath, err := c.Prompt.InputExistingFilePath("Automate FQDN Root CA File Path [Eg: /home/ubuntu/fqdn2-root-ca.pem]")
 	if err != nil {
 		return
 	}
@@ -980,7 +980,7 @@ func (c *HaDeployConfigGen) PromptSshPort() (err error) {
 }
 
 func (c *HaDeployConfigGen) PromptSshKey() (err error) {
-	sshKeyFile, err := c.Prompt.InputExistingFilePathDefault("SSH Key File", "~/.ssh/id_rsa")
+	sshKeyFile, err := c.Prompt.InputExistingFilePath("SSH Key File Path [*.pem downloaded for SSH Key Pair from AWS]")
 	if err != nil {
 		return
 	}
@@ -1021,11 +1021,11 @@ func (c *HaDeployConfigGen) DefaultExistingInfraValues() {
 }
 
 func (c *HaDeployConfigGen) PromptBackup() (err error) {
-	isBackupNeeded, err := c.Prompt.Confirm("Backup need to be configured during deployment", "yes", "no")
+	isBackupNeeded, err := c.Prompt.Confirm("Configured backup 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)
+		_, backupOption, err1 := c.Prompt.Select("Choose backup option", AWS_S3, MINIO, OBJECT_STORE, FILE_SYSTEM, NFS, EFS)
 		if err1 != nil {
 			return err1
 		}
@@ -1056,7 +1056,7 @@ func (c *HaDeployConfigGen) PromptBackup() (err error) {
 
 func (c *HaDeployConfigGen) PromptObjectStorageSettings(backupOption string) (err error) {
 
-	bucketName, err := c.Prompt.InputStringRegex("Backup bucket name", BUCKET_NAME_REGEX)
+	bucketName, err := c.Prompt.InputStringRegex("Bucket name", BUCKET_NAME_REGEX)
 	if err != nil {
 		return
 	}
diff --git a/lib/config/hadeployconfig.go b/lib/config/hadeployconfig.go
index b9b4f83df..21afc8d3d 100644
--- a/lib/config/hadeployconfig.go
+++ b/lib/config/hadeployconfig.go
@@ -149,9 +149,9 @@ type ConfigAwsSettings struct {
 	ManagedOpensearchUsername     string   `toml:"managed_opensearch_username,omitempty"`
 	ManagedOpensearchUserPassword string   `toml:"managed_opensearch_user_password,omitempty"`
 	ManagedOpensearchCertificate  string   `toml:"managed_opensearch_certificate,omitempty"`
-	AwsOsSnapshotRoleArn          string   `toml:"aws_os_snapshot_role_arn,omitempty"`
-	OsSnapshotUserAccessKeyID     string   `toml:"os_snapshot_user_access_key_id,omitempty"`
-	OsSnapshotUserAccessKeySecret string   `toml:"os_snapshot_user_access_key_secret,omitempty"`
+	AwsOsSnapshotRoleArn          string   `toml:"aws_os_snapshot_role_arn"`
+	OsSnapshotUserAccessKeyID     string   `toml:"os_snapshot_user_access_key_id"`
+	OsSnapshotUserAccessKeySecret string   `toml:"os_snapshot_user_access_key_secret"`
 	ManagedRdsInstanceURL         string   `toml:"managed_rds_instance_url,omitempty"`
 	ManagedRdsSuperuserUsername   string   `toml:"managed_rds_superuser_username,omitempty"`
 	ManagedRdsSuperuserPassword   string   `toml:"managed_rds_superuser_password,omitempty"`
diff --git a/lib/pmt/prompt.go b/lib/pmt/prompt.go
index f3a6c374a..1c5183832 100644
--- a/lib/pmt/prompt.go
+++ b/lib/pmt/prompt.go
@@ -15,10 +15,11 @@ import (
 )
 
 const (
-	FAIL_MSG         = "prompt failed %v"
-	INVALID_INT      = "invalid int"
-	PARSE_INT_FAIL   = "parse int failed %v"
-	REGEX_CHECK_FAIL = "regex check failed (%v) "
+	FAIL_MSG                            = "prompt failed %v"
+	INVALID_INT                         = "invalid int"
+	PARSE_INT_FAIL                      = "parse int failed %v"
+	REGEX_CHECK_FAIL                    = "input value failed to matched with expected pattern (%v)"
+	REGEX_CHECK_FAIL_WITH_SAMPLE_STRING = "regex check failed. Sample Value: %v"
 )
 const esc = "\033["
 const moveDown = esc + "1B"
diff --git a/products.meta b/products.meta
index e0ff9eedc..828ac9e9f 100644
--- a/products.meta
+++ b/products.meta
@@ -73,8 +73,9 @@
       "services": [
         "chef/backup-gateway",
         "chef/deployment-service",
+        "chef/cereal-service",
         "chef/license-control-service",
-        "chef/automate-load-balancer"
+        "chef/automate-load-balancer"     
       ],
       "packages": [
         "chef/automate-cli",
diff --git a/scripts/cert.sh b/scripts/cert.sh
index 47203a198..8a15223ee 100755
--- a/scripts/cert.sh
+++ b/scripts/cert.sh
@@ -13,7 +13,7 @@ openssl genrsa -out admin.key 2048
 openssl req -new -key admin.key -out admin.csr -subj '/C=US/ST=Washington/L=Seattle/O=Chef Software Inc/CN=chefadmin'
  
 #Use the CSR to generate the signed node Certificate:
-openssl x509 -extfile <(printf "subjectAltName=DNS:chefadmin") -req -in admin.csr -CA MyRootCA.pem -CAkey MyRootCA.key -CAcreateserial -out admin.pem -sha256
+openssl x509 -extfile <(printf "subjectAltName=DNS:chefadmin") -req -in admin.csr -CA MyRootCA.pem -CAkey MyRootCA.key -CAcreateserial -out admin.pem -sha256 -days 3660
  
 # root pem cert that signed the below cert/key pairs below
 # Used for hab_sup_http_gateway_ca_cert 


Test patch:
diff --git a/components/automate-cli/cmd/chef-automate/verify_test.go b/components/automate-cli/cmd/chef-automate/verify_test.go
index eaae0c9dd..fe5d37bec 100644
--- a/components/automate-cli/cmd/chef-automate/verify_test.go
+++ b/components/automate-cli/cmd/chef-automate/verify_test.go
@@ -208,7 +208,7 @@ func TestRunVerifyCmd(t *testing.T) {
 			wantErr:    nil,
 		},
 		{
-			description: "bastion without automate-verify - success",
+			description: "bastion without automate-verify",
 			mockHttputils: &httputils.MockHTTPClient{
 				MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) {
 					if strings.Contains(url, "batch-check") {
@@ -259,7 +259,7 @@ func TestRunVerifyCmd(t *testing.T) {
 				},
 			},
 			configFile: CONFIG_TOML_PATH + CONFIG_FILE,
-			wantErr:    nil,
+			wantErr:    errors.New("This might be due to not enabling the 7799 port"),
 		},
 		{
 			description: "Failed to get automate HA infra details",
@@ -379,6 +379,8 @@ func TestRunVerifyCmd(t *testing.T) {
 
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
+			oldBuildTimeVal := version.BuildTime
+			version.BuildTime = "20230622174934"
 			ConvTfvarToJsonFunc = tt.ConvTfvarToJsonFunc
 			cw := majorupgrade_utils.NewCustomWriter()
 
@@ -402,6 +404,7 @@ func TestRunVerifyCmd(t *testing.T) {
 			} else {
 				assert.NoError(t, err)
 			}
+			version.BuildTime = oldBuildTimeVal
 		})
 	}
 }
diff --git a/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn_test.go b/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn_test.go
index b8ab1c85c..c26c88c4d 100644
--- a/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn_test.go
+++ b/components/automate-cli/pkg/verifyserver/server/api/v1/fqdn_test.go
@@ -5,6 +5,7 @@ import (
 	"net/http/httptest"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants"
 	"github.com/chef/automate/components/automate-cli/pkg/verifyserver/models"
@@ -19,7 +20,7 @@ import (
 
 func SetupMockFqdnService() fqdnservice.IFqdnService {
 	return &fqdnservice.MockFqdnService{
-		CheckFqdnReachabilityFunc: func(req models.FqdnRequest, port string) models.FqdnResponse {
+		CheckFqdnReachabilityFunc: func(req models.FqdnRequest, port string, duration time.Duration) models.FqdnResponse {
 			return models.FqdnResponse{
 				Passed: true,
 				Checks: []models.Checks{
diff --git a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go
index be31cca5a..cdd9738fd 100644
--- a/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/batchcheckservice/trigger/sshuseraccesschecktrigger/sshuseraccesschecktrigger_test.go
@@ -439,7 +439,7 @@ func TestGetSShUserAPIRquest(t *testing.T) {
 		assert.Equal(t, err.Error(), actualerr.Error())
 	})
 
-	t.Run("Permission is greater the 400", func(t *testing.T) {
+	t.Run("Permission is greater than 600", func(t *testing.T) {
 		fwc := NewSshUserAccessCheck(logger.NewLogrusStandardLogger(), &fileutils.MockFileSystemUtils{
 			ReadFileFunc: func(filepath string) ([]byte, error) {
 				return []byte("test_key"), nil
@@ -449,7 +449,7 @@ func TestGetSShUserAPIRquest(t *testing.T) {
 			},
 		}, "1234")
 		ip := "1.2.3.4"
-		expected, err := models.SShUserRequest{}, errors.New("Provide permission on the SSH key file as 400 (Read Only by Owner).")
+		expected, err := models.SShUserRequest{}, errors.New("Provide permission on the SSH key file as 400 (Read Only by Owner) or 600 (Read and Write by Owner).")
 		actual, actualerr := fwc.getSShUserAPIRequest(ip, GetRequestJson().SSHUser)
 		assert.Equal(t, expected, actual)
 		assert.Equal(t, err.Error(), actualerr.Error())
@@ -469,6 +469,21 @@ func TestGetSShUserAPIRquest(t *testing.T) {
 		assert.Equal(t, expected, actual)
 	})
 
+	t.Run("Reading was successfull with 600 permissions", func(t *testing.T) {
+		fwc := NewSshUserAccessCheck(logger.NewLogrusStandardLogger(), &fileutils.MockFileSystemUtils{
+			ReadFileFunc: func(filepath string) ([]byte, error) {
+				return []byte("test_key"), nil
+			},
+			GetFilePermissionFunc: func(filePath string) (int64, error) {
+				return 600, nil
+			},
+		}, "1234")
+		ip := "1.2.3.4"
+		expected := GetSshRequest()
+		actual, _ := fwc.getSShUserAPIRequest(ip, GetRequestJson().SSHUser)
+		assert.Equal(t, expected, actual)
+	})
+
 	t.Run("Reading was unsuccessfull", func(t *testing.T) {
 		fwc := NewSshUserAccessCheck(logger.NewLogrusStandardLogger(), &fileutils.MockFileSystemUtils{
 			ReadFileFunc: func(filepath string) ([]byte, error) {
diff --git a/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice_test.go b/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice_test.go
index 7aa168dfa..feffddd44 100644
--- a/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/fqdnservice/fqdnservice_test.go
@@ -730,7 +730,7 @@ func TestCheckFqdnReachability(t *testing.T) {
 
 	for _, e := range tests {
 		t.Run(e.TestName, func(t *testing.T) {
-			res := fq.CheckFqdnReachability(e.ReqBody, e.Port)
+			res := fq.CheckFqdnReachability(e.ReqBody, e.Port, time.Second*2)
 			assert.Equal(t, e.ResponseBody, res)
 		})
 	}
@@ -744,6 +744,6 @@ func TestMakeConcurrentCalls(t *testing.T) {
 		Timeout:   2 * time.Second,
 	}
 	url := fmt.Sprintf("https://%v:6574", LOCALHOST2)
-	res := fq.MakeConcurrentCalls(url, client, make(map[string]int))
+	res := fq.MakeConcurrentCalls(url, client, make(map[string]int), time.Second*2)
 	assert.Equal(t, errors.New("nodes are not reachable"), res)
 }
diff --git a/components/automate-cli/pkg/verifyserver/services/sshusercheckservice/sshusercheckservice_test.go b/components/automate-cli/pkg/verifyserver/services/sshusercheckservice/sshusercheckservice_test.go
index 0162ab9db..3014fe818 100644
--- a/components/automate-cli/pkg/verifyserver/services/sshusercheckservice/sshusercheckservice_test.go
+++ b/components/automate-cli/pkg/verifyserver/services/sshusercheckservice/sshusercheckservice_test.go
@@ -201,7 +201,7 @@ func TestCheckSshConnection(t *testing.T) {
 					Passed:        false,
 					SuccessMsg:    "",
 					ErrorMsg:      "SSH user is unaccessible for the node with IP: " + nodeIp,
-					ResolutionMsg: "Give SSH access to the user with the give key on the node: " + nodeIp,
+					ResolutionMsg: "Give SSH access to the user with the given key on the node: " + nodeIp,
 				},
 				{
 					Title:         FailureSudoPassword,
diff --git a/components/license-control-service/licenseaudit/licenseauditworkflow_test.go b/components/license-control-service/licenseaudit/licenseauditworkflow_test.go
new file mode 100644
index 000000000..278ea07e5
--- /dev/null
+++ b/components/license-control-service/licenseaudit/licenseauditworkflow_test.go
@@ -0,0 +1,453 @@
+package licenseaudit
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/chef/automate/lib/cereal"
+	"github.com/stretchr/testify/assert"
+)
+
+type TestExecute struct {
+	wantError bool
+}
+
+func (test TestExecute) Execute(command string) (string, error) {
+	if test.wantError {
+		return "", errors.New("Received Error from command : exit status 1")
+	}
+	return command, nil
+}
+
+func Test_OnStart(t *testing.T) {
+	tests := []struct {
+		name                  string
+		isGetParameterFailure bool
+		isEnqueueFailure      bool
+		isFailedExpected      bool
+		isContinueExpected    bool
+		expectedError         string
+	}{
+		{
+			name:                  "testOnStart_Success",
+			isGetParameterFailure: false,
+			isEnqueueFailure:      false,
+			isFailedExpected:      false,
+			isContinueExpected:    true,
+			expectedError:         "",
+		},
+		{
+			name:                  "testOnStart_GetParametsFailure",
+			isGetParameterFailure: true,
+			isEnqueueFailure:      false,
+			isFailedExpected:      true,
+			isContinueExpected:    false,
+			expectedError:         "failed to unmarshal license-audit workflow parameters: error in fetching parameters",
+		},
+		{
+			name:               "testOnStart_Enqueue_Fail",
+			isEnqueueFailure:   true,
+			isFailedExpected:   true,
+			isContinueExpected: false,
+			expectedError:      "failed to enqueue the license-audit task: error in enqueuing",
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+
+			workFlow := LicenseAuditWorkflow{}
+
+			workflowInstance := &CerealWorkflow{
+				t:                 t,
+				failEnqueueTask:   tc.isEnqueueFailure,
+				failGetParameters: tc.isGetParameterFailure,
+			}
+
+			result := workFlow.OnStart(workflowInstance, cereal.StartEvent{})
+			assert.Equal(t, tc.isFailedExpected, result.IsFailed())
+			assert.Equal(t, tc.isContinueExpected, result.IsContinuing())
+			if tc.isFailedExpected {
+				assert.Error(t, result.Err())
+				assert.Equal(t, tc.expectedError, result.Err().Error())
+			}
+		})
+	}
+}
+
+type CerealWorkflow struct {
+	t                 *testing.T
+	failGetParameters bool
+	failEnqueueTask   bool
+	failGetPayload    bool
+	isRetriesLeft     bool
+	isRetryTest       bool
+}
+
+func (c CerealWorkflow) GetPayload(obj interface{}) error {
+	if c.failGetPayload {
+		return fmt.Errorf("Error in fetching payload")
+	}
+
+	var jsonString string
+	if c.isRetriesLeft {
+		jsonString = `{"Retries":3}`
+	} else {
+		jsonString = `{"Retries":0}`
+	}
+
+	jsonBytes := []byte(jsonString)
+	err := json.Unmarshal(jsonBytes, obj)
+	return err
+}
+
+func (c CerealWorkflow) GetParameters(obj interface{}) error {
+	if c.failGetParameters {
+		return fmt.Errorf("error in fetching parameters")
+	}
+	jsonString := `{"Retries":3}`
+	jsonBytes := []byte(jsonString)
+	err := json.Unmarshal(jsonBytes, obj)
+	return err
+}
+
+func (c CerealWorkflow) EnqueueTask(taskName cereal.TaskName, parameters interface{}, opts ...cereal.TaskEnqueueOpt) error {
+	if c.failEnqueueTask {
+		return fmt.Errorf("error in enqueuing")
+	}
+	assert.Equal(c.t, "license-audit-task", taskName.String())
+	return nil
+}
+
+func (c CerealWorkflow) Complete(opt ...cereal.CompleteOpt) cereal.Decision {
+	return cereal.NewCompleteDecision("")
+}
+
+func (c CerealWorkflow) Continue(payload interface{}) cereal.Decision {
+	workflowPayload := payload.(*AuditWorkflowPayload)
+	// assert.Equal(c.t, true, workflowPayload.ControlIndexFlag)
+	if c.isRetryTest {
+		assert.Equal(c.t, 2, workflowPayload.Retries)
+	} else {
+		assert.Equal(c.t, 3, workflowPayload.Retries)
+	}
+	return cereal.NewContinueDecision(payload)
+}
+
+func (c CerealWorkflow) Fail(err error) cereal.Decision {
+	return cereal.NewFailDecision(err)
+}
+
+func (c CerealWorkflow) InstanceName() string {
+	return ""
+}
+
+func (c CerealWorkflow) TotalEnqueuedTasks() int {
+	return 1
+}
+
+func (c CerealWorkflow) TotalCompletedTasks() int {
+	return 1
+}
+
+type TaskResult struct {
+	isError         bool
+	taskResultError bool
+}
+
+func (r *TaskResult) GetParameters(obj interface{}) error {
+	return nil
+}
+
+func (r *TaskResult) Get(obj interface{}) error {
+	if r.taskResultError {
+		return fmt.Errorf("Error in fetching job results")
+	}
+	// jsonString := `{"JobID":"1234-5678","ReportSize":8200,"PreSignedURL":"www.test.com/object"}`
+	// jsonBytes := []byte(jsonString)
+	// err := json.Unmarshal(jsonBytes, obj)
+	// return err
+	return nil
+}
+
+func (r *TaskResult) Err() error {
+	if r.isError {
+		return fmt.Errorf("error in task execution")
+	}
+	return nil
+}
+
+func Test_OnTaskComplete(t *testing.T) {
+	tests := []struct {
+		name                 string
+		isGetPayloadFailure  bool
+		isEnqueueFailure     bool
+		isFailedExpected     bool
+		isContinueExpected   bool
+		isCompleteExpected   bool
+		isRetriesLeft        bool
+		isTaskError          bool
+		isRetryTest          bool
+		isParameterFailure   bool
+		expectedError        string
+		isFetchTaskResultErr bool
+	}{
+		{
+			name:                "testOnTaskComplete_Success",
+			isGetPayloadFailure: false,
+			isEnqueueFailure:    false,
+			isFailedExpected:    false,
+			isContinueExpected:  false,
+			isCompleteExpected:  true,
+			isRetriesLeft:       true,
+			expectedError:       "",
+		},
+		{
+			name:                "testOnTaskComplete_GetPayload_Fail",
+			isGetPayloadFailure: true,
+			isEnqueueFailure:    false,
+			isFailedExpected:    true,
+			isContinueExpected:  false,
+			isCompleteExpected:  false,
+			expectedError:       "failed to unmarshal license-audit payload in OnComplete: Error in fetching payload",
+		},
+		{
+			name:                "testOnTaskComplete_NoRetriesLeft_Fail",
+			isGetPayloadFailure: false,
+			isEnqueueFailure:    false,
+			isFailedExpected:    true,
+			isContinueExpected:  false,
+			isCompleteExpected:  false,
+			isRetriesLeft:       false,
+			isTaskError:         true,
+			expectedError:       "failed to run license-audit: error in task execution",
+		},
+		{
+			name:                 "testOnTaskComplete_GetTaskResult_fail_Retries_left",
+			isGetPayloadFailure:  false,
+			isEnqueueFailure:     false,
+			isFailedExpected:     true,
+			isContinueExpected:   false,
+			isCompleteExpected:   false,
+			isRetriesLeft:        true,
+			isFetchTaskResultErr: true,
+			expectedError:        "failed to get the task run result in OnTaskComplete for license audit: Error in fetching job results",
+		},
+		{
+			name:                "testOnTaskComplete_RetriesLeft_Success",
+			isGetPayloadFailure: false,
+			isEnqueueFailure:    false,
+			isFailedExpected:    false,
+			isContinueExpected:  true,
+			isCompleteExpected:  false,
+			isRetriesLeft:       true,
+			isTaskError:         true,
+			isRetryTest:         true,
+			expectedError:       "",
+		},
+		{
+			name:                "testOnTaskComplete_RetriesLeft_GetParameter_Fail",
+			isGetPayloadFailure: false,
+			isEnqueueFailure:    false,
+			isFailedExpected:    true,
+			isContinueExpected:  false,
+			isCompleteExpected:  false,
+			isRetriesLeft:       true,
+			isTaskError:         true,
+			isRetryTest:         true,
+			isParameterFailure:  true,
+			expectedError:       "failed to unmarshal license-audit workflow parameters: error in fetching parameters",
+		},
+		{
+			name:                "testOnTaskComplete_RetriesLeft_GetParameter_Fail",
+			isGetPayloadFailure: false,
+			isEnqueueFailure:    false,
+			isFailedExpected:    true,
+			isContinueExpected:  false,
+			isCompleteExpected:  false,
+			isRetriesLeft:       true,
+			isTaskError:         true,
+			isRetryTest:         true,
+			isParameterFailure:  true,
+			expectedError:       "failed to unmarshal license-audit workflow parameters: error in fetching parameters",
+		},
+		{
+			name:                "testOnTaskComplete_RetriesLeft_EnqueueFail",
+			isGetPayloadFailure: false,
+			isEnqueueFailure:    true,
+			isFailedExpected:    true,
+			isContinueExpected:  false,
+			isCompleteExpected:  false,
+			isRetriesLeft:       true,
+			isTaskError:         true,
+			isRetryTest:         true,
+			expectedError:       "failed to enqueue the license-audit task: error in enqueuing",
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+
+			workFlow := LicenseAuditWorkflow{}
+
+			workflowInstance := &CerealWorkflow{
+				t:                 t,
+				isRetryTest:       tc.isRetryTest,
+				failGetPayload:    tc.isGetPayloadFailure,
+				isRetriesLeft:     tc.isRetriesLeft,
+				failGetParameters: tc.isParameterFailure,
+				failEnqueueTask:   tc.isEnqueueFailure,
+			}
+
+			result := workFlow.OnTaskComplete(workflowInstance, cereal.TaskCompleteEvent{
+				Result: &TaskResult{
+					isError:         tc.isTaskError,
+					taskResultError: tc.isFetchTaskResultErr,
+				},
+			})
+			assert.Equal(t, tc.isFailedExpected, result.IsFailed())
+			assert.Equal(t, tc.isContinueExpected, result.IsContinuing())
+			assert.Equal(t, tc.isCompleteExpected, result.IsComplete())
+			if tc.isFailedExpected {
+				assert.Error(t, result.Err())
+				assert.Equal(t, tc.expectedError, result.Err().Error())
+			}
+		})
+	}
+}
+
+func Test_getAppendedCommand(t *testing.T) {
+	tests := []struct {
+		name    string
+		command string
+		want    string
+	}{
+		{
+			name:    "Checking the output of the string",
+			command: "%s %s",
+		},
+		{
+			name:    "Getting license command",
+			command: Command,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			dateToBeAppended := time.Now().AddDate(0, 0, -1).UTC().Format("2006-01-02")
+			tt.want = fmt.Sprintf(tt.command, dateToBeAppended, dateToBeAppended, OutputFileName)
+			got := getAppendedCommand(tt.command)
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func Test_executeCommandforAudit(t *testing.T) {
+	type args struct {
+		executeCommand ExecuteCommand
+		command        string
+	}
+	tests := []struct {
+		name    string
+		args    args
+		want    string
+		wantErr bool
+		err     error
+	}{
+		{
+			name:    "Getting output without error",
+			args:    args{executeCommand: &TestExecute{}, command: "test"},
+			want:    "test",
+			wantErr: false,
+		},
+		{
+			name:    "Getting error from command",
+			args:    args{executeCommand: &TestExecute{wantError: true}, command: ""},
+			want:    "",
+			wantErr: true,
+			err:     errors.New("Received Error from command : exit status 1"),
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := executeCommandforAudit(tt.args.executeCommand, tt.args.command)
+			assert.Equal(t, tt.want, got)
+			if !tt.wantErr {
+				assert.Empty(t, err)
+			} else {
+				assert.NotEmpty(t, err)
+				assert.Equal(t, tt.err, err)
+			}
+		})
+	}
+}
+
+type CerealTask struct {
+	isParameterFailure bool
+}
+
+func (t *CerealTask) GetParameters(obj interface{}) error {
+	if t.isParameterFailure {
+		return fmt.Errorf("error in fetching parameters")
+	}
+	return nil
+}
+
+func (t *CerealTask) GetMetadata() cereal.TaskMetadata {
+	return cereal.TaskMetadata{}
+}
+
+func Test_Run(t *testing.T) {
+	tests := []struct {
+		name                   string
+		isGetParametersFailure bool
+		expectedErrorString    error
+		commandError           bool
+		expectedError          bool
+	}{
+		{
+			name:                   "Test Run Success",
+			isGetParametersFailure: false,
+			commandError:           false,
+			expectedError:          false,
+			expectedErrorString:    nil,
+		},
+		{
+			name:                   "Task Run paramaters failure",
+			isGetParametersFailure: true,
+			commandError:           false,
+			expectedError:          true,
+			expectedErrorString:    errors.New("Unable to marshal Task Parameters for Audit task: error in fetching parameters"),
+		},
+		{
+			name:                   "Task Run output from command failure",
+			isGetParametersFailure: false,
+			commandError:           true,
+			expectedError:          true,
+			expectedErrorString:    errors.New("Received Error from command : exit status 1"),
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+
+			task := &LicenseAuditTask{
+				ExecuteCommand: &TestExecute{wantError: tc.commandError},
+				Command:        "testingcommand",
+			}
+			result, err := task.Run(context.Background(), &CerealTask{
+				isParameterFailure: tc.isGetParametersFailure,
+			})
+
+			if tc.expectedError {
+				assert.Equal(t, tc.expectedErrorString.Error(), err.Error())
+			} else {
+				assert.NoError(t, err)
+				assert.NotNil(t, result)
+			}
+		})
+	}
+}
diff --git a/lib/config/genconfig/awshaprovisionconfig_test.go b/lib/config/genconfig/awshaprovisionconfig_test.go
index 53d01bad2..a12562464 100644
--- a/lib/config/genconfig/awshaprovisionconfig_test.go
+++ b/lib/config/genconfig/awshaprovisionconfig_test.go
@@ -5,10 +5,12 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"net/http"
 	"os"
 	"testing"
 
 	"github.com/chef/automate/lib/config"
+	"github.com/chef/automate/lib/httputils"
 	"github.com/chef/automate/lib/io/fileutils"
 	"github.com/chef/automate/lib/ioutils"
 	"github.com/chef/automate/lib/pmt"
@@ -47,6 +49,8 @@ func TestPromptsCidrAWSManaged(t *testing.T) {
 	enableCustomCerts := false
 	input(b, "\r")
 
+	input(b, moveDown+"\r")
+
 	awsProfile := "default"
 	input(b, "\r")
 
@@ -145,6 +149,9 @@ func TestPromptsCidrAWSManaged(t *testing.T) {
 	osDefaultCerts := true
 	input(b, "\r")
 
+	// To not skip role cred
+	input(b, moveDown+"\r")
+
 	osSnapshotRoleArn := "arn-os-snapshot-role"
 	input(b, osSnapshotRoleArn+"\r")
 
@@ -208,6 +215,21 @@ func TestPromptsCidrAWSManaged(t *testing.T) {
 		Prompt:    p,
 		FileUtils: mfsu,
 		Config:    &config.HaDeployConfig{},
+		httpRequestClient: &httputils.MockHTTPClient{
+			MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+				if url == TOKEN_URLS {
+					return &http.Response{
+						StatusCode: 200,
+					}, []byte("dummytoken"), nil
+				}
+				return &http.Response{StatusCode: 200}, []byte(`{
+					"Code" : "Success",
+					"LastUpdated" : "2023-08-09T11:23:21Z",
+					"InstanceProfileArn" : "arn:aws:iam::1234:instance-profile/bastion-role-user",
+					"InstanceProfileId" : "AIPARHU"
+				}`), nil
+			},
+		},
 	}
 
 	err := c.Prompts()
@@ -1124,6 +1146,21 @@ func testPromptsCidrAWSManagedErrRun(t *testing.T, upto int) {
 		Prompt:    p,
 		FileUtils: mfsu,
 		Config:    &config.HaDeployConfig{},
+		httpRequestClient: &httputils.MockHTTPClient{
+			MakeRequestWithHeadersfunc: func(requestMethod, url string, body interface{}, headerkey, headerValue string) (*http.Response, []byte, error) {
+				if url == TOKEN_URLS {
+					return &http.Response{
+						StatusCode: 200,
+					}, []byte("dummytoken"), nil
+				}
+				return &http.Response{StatusCode: 200}, []byte(`{
+					"Code" : "Success",
+					"LastUpdated" : "2023-08-09T11:23:21Z",
+					"InstanceProfileArn" : "arn:aws:iam::1234:instance-profile/bastion-role-user",
+					"InstanceProfileId" : "AIPARHU"
+				}`), nil
+			},
+		},
 	}
 
 	err := c.Prompts()
@@ -1156,6 +1193,9 @@ func callInput(upto int, b *bytes.Buffer) {
 	if upto >= 5 {
 		input(b, "\r")
 	}
+	if upto >= 5 {
+		input(b, moveDown+"\r")
+	}
 	if upto >= 6 {
 		input(b, "\r")
 	}