Skip to content

Commit

Permalink
feat: refactor UX for lacework generate cloud-account aws (#1448)
Browse files Browse the repository at this point in the history
* feat: refactor UX for lacework generate cloud-account aws

* chore: fix make test

* test: add more integration test

* chore: pr feedback

* chore: fix typo

* feat: Add AWS Configuration Org deployment into generate command (#1451)

* feat: Add AWS Configuration Org deployment into generate command

Co-authored-by: Matt Cadorette <matthew.cadorette@lacework.net>

---------

Co-authored-by: Matt Cadorette <matthew.cadorette@lacework.net>
  • Loading branch information
PengyuanZhao and Matt Cadorette authored Nov 16, 2023
1 parent 6ca5341 commit 222660b
Show file tree
Hide file tree
Showing 12 changed files with 1,904 additions and 1,752 deletions.
8 changes: 8 additions & 0 deletions cli/cmd/cli_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package cmd

import (
"fmt"
"os"

"github.com/AlecAivazis/survey/v2"
Expand All @@ -35,6 +36,13 @@ var promptIconsFunc = func(icons *survey.IconSet) {
icons.Question.Text = "▸"
}

// customPromptIconsFunc configures the prompt icons with custom string for Unix systems
var customPromptIconsFunc = func(s string) func(icons *survey.IconSet) {
return func(icons *survey.IconSet) {
icons.Question.Text = fmt.Sprintf("▸ %s", s)
}
}

// A variety of colorized icons used throughout the code
var (
successIcon = color.HiGreenString("✓")
Expand Down
8 changes: 8 additions & 0 deletions cli/cmd/cli_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package cmd

import (
"fmt"
"os"

"github.com/AlecAivazis/survey/v2"
Expand All @@ -33,6 +34,13 @@ var promptIconsFunc = func(icons *survey.IconSet) {
icons.Question.Text = ">"
}

// customPromptIconsFunc configures the prompt icons with custom string for Windows systems
var customPromptIconsFunc = func(s string) func(icons *survey.IconSet) {
return func(icons *survey.IconSet) {
icons.Question.Text = fmt.Sprintf("> %s", s)
}
}

// A variety of colorized icons used throughout the code
var (
successIcon = color.HiGreenString("√")
Expand Down
21 changes: 6 additions & 15 deletions cli/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type SurveyQuestionWithValidationArgs struct {
Response interface{}
Opts []survey.AskOpt
Required bool
Icon string
}

// SurveyQuestionInteractiveOnly Prompt use for question, only if the CLI is in interactive mode
Expand All @@ -82,7 +83,11 @@ func SurveyQuestionInteractiveOnly(question SurveyQuestionWithValidationArgs) er
}

// Add custom icon
question.Opts = append(question.Opts, survey.WithIcons(promptIconsFunc))
if question.Icon != "" {
question.Opts = append(question.Opts, survey.WithIcons(customPromptIconsFunc(question.Icon)))
} else {
question.Opts = append(question.Opts, survey.WithIcons(promptIconsFunc))
}

// If noninteractive is not set, ask the question
if !cli.nonInteractive {
Expand Down Expand Up @@ -217,20 +222,6 @@ func validateOutputLocation(dirname string) error {
return nil
}

func validateAwsSubAccounts(subaccounts []string) error {
// validate the format of supplied values is correct
for _, account := range subaccounts {
if ok, err := regexp.MatchString(ValidateSubAccountFlagRegex, account); !ok {
if err != nil {
return errors.Wrap(err, "failed to validate supplied subaccount format")
}
return errors.New("supplied aws subaccount in invalid format")
}
}

return nil
}

// validateStringWithRegex create survey.Validator for string with regex
func validateStringWithRegex(val interface{}, regex string, errorString string) error {
switch value := val.(type) {
Expand Down
1,325 changes: 716 additions & 609 deletions cli/cmd/generate_aws.go

Large diffs are not rendered by default.

30 changes: 16 additions & 14 deletions cli/cmd/generate_aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestGenerateMostBasicArgs(t *testing.T) {
data.Cloudtrail = true
data.Config = true
data.AwsRegion = "us-east-2"
err := promptAwsGenerate(&data, &aws.ExistingIamRoleDetails{}, &AwsGenerateCommandExtraState{Output: "/tmp"})
err := promptAwsGenerate(&data, &aws.AwsGenerateCommandExtraState{Output: "/tmp"})

assert.Nil(t, err)
}
Expand All @@ -34,9 +34,11 @@ func TestMissingValidEntityToConfigure(t *testing.T) {
defer toggleNonInteractive()

data := aws.GenerateAwsTfConfigurationArgs{}
err := promptAwsGenerate(&data, &aws.ExistingIamRoleDetails{}, &AwsGenerateCommandExtraState{Output: "/tmp"})
err := promptAwsGenerate(&data, &aws.AwsGenerateCommandExtraState{Output: "/tmp"})
assert.Nil(t, err)
err = data.Validate()
assert.Error(t, err)
assert.Equal(t, "must enable agentless, cloudtrail or config", err.Error())
assert.Equal(t, "Agentless, CloudTrail or Config integration must be enabled", err.Error())
}

func TestArnRegex(t *testing.T) {
Expand Down Expand Up @@ -96,9 +98,9 @@ func TestGenerationCache(t *testing.T) {
defer os.RemoveAll(dir)
cli.InitCache(dir)

extraState := &AwsGenerateCommandExtraState{}
extraState.writeCache()
assert.NoFileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAssetAwsExtraState)))
extraState := &aws.AwsGenerateCommandExtraState{}
writeExtraStateCache(extraState)
assert.NoFileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAwsExtraStateKey)))
})
t.Run("extra state should be written if not empty", func(t *testing.T) {
dir, err := os.MkdirTemp("", "lacework-cli-cache")
Expand All @@ -108,9 +110,9 @@ func TestGenerationCache(t *testing.T) {
defer os.RemoveAll(dir)
cli.InitCache(dir)

extraState := AwsGenerateCommandExtraState{Output: "/tmp"}
extraState.writeCache()
assert.FileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAssetAwsExtraState)))
extraState := &aws.AwsGenerateCommandExtraState{Output: "/tmp"}
writeExtraStateCache(extraState)
assert.FileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAwsExtraStateKey)))
})
t.Run("iac params should not be cached when empty", func(t *testing.T) {
dir, err := os.MkdirTemp("", "lacework-cli-cache")
Expand All @@ -121,8 +123,8 @@ func TestGenerationCache(t *testing.T) {
cli.InitCache(dir)

args := aws.GenerateAwsTfConfigurationArgs{}
writeAwsGenerationArgsCache(&args)
assert.NoFileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAwsAssetIacParams)))
writeArgsCache(&args)
assert.NoFileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAwsArgsKey)))
})
t.Run("iac params should be cached when not empty", func(t *testing.T) {
dir, err := os.MkdirTemp("", "lacework-cli-cache")
Expand All @@ -132,8 +134,8 @@ func TestGenerationCache(t *testing.T) {
defer os.RemoveAll(dir)
cli.InitCache(dir)

args := aws.GenerateAwsTfConfigurationArgs{AwsRegion: "us-east-2"}
writeAwsGenerationArgsCache(&args)
assert.FileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAwsAssetIacParams)))
args := aws.GenerateAwsTfConfigurationArgs{AwsRegion: "us-east-2", Agentless: true}
writeArgsCache(&args)
assert.FileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAwsArgsKey)))
})
}
12 changes: 10 additions & 2 deletions cli/docs/lacework_generate_cloud-account_aws.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ lacework generate cloud-account aws [flags]
```
--agentless enable agentless integration
--agentless_management_account_id string AWS management account ID for Agentless integration
--agentless_monitored_account_ids strings AWS monitored account IDs for Agentless integrations
--agentless_monitored_account_ids strings AWS monitored account IDs for Agentless integrations; may contain account IDs, OUs, or the organization root (e.g. 123456789000,ou-abcd-12345678,r-abcd)
--agentless_monitored_accounts strings AWS monitored accounts for Agentless integrations; value format must be <aws profile>:<region>
--agentless_scanning_accounts strings AWS scanning accounts for Agentless integrations; value format must be <aws profile>:<region>
--apply run terraform apply without executing plan or prompting
--aws_assume_role string specify aws assume role
--aws_organization enable organization integration
Expand All @@ -53,7 +55,13 @@ lacework generate cloud-account aws [flags]
--cloudtrail_name string specify name of cloudtrail integration
--cloudtrail_org_account_mapping string Org account mapping json string. Example: '{"default_lacework_account":"main", "mapping": [{ "aws_accounts": ["123456789011"], "lacework_account": "sub-account-1"}]}'
--config enable config integration
--config_name string specify name of config integration
--config_cf_resource_prefix string specify Cloudformation resource prefix for Config organization integration
--config_lacework_access_key_id string specify AWS access key ID for Config organization integration
--config_lacework_account string specify lacework account for Config organization integration
--config_lacework_secret_key string specify AWS secret key for Config organization integration
--config_lacework_sub_account string specify lacework sub-account for Config organization integration
--config_organization_id string specify AWS organization ID for Config organization integration
--config_organization_units strings specify AWS organization units for Config organization integration
--consolidated_cloudtrail use consolidated trail
--existing_bucket_arn string specify existing cloudtrail S3 bucket ARN
--existing_iam_role_arn string specify existing iam role arn to use
Expand Down
Loading

0 comments on commit 222660b

Please sign in to comment.