Skip to content

Commit

Permalink
feat(cli): CLI guided config support for creating Azure AD integration (
Browse files Browse the repository at this point in the history
  • Loading branch information
Manan-Bhatia-0 authored Aug 15, 2024
1 parent d25c70d commit e5ecd21
Show file tree
Hide file tree
Showing 12 changed files with 461 additions and 110 deletions.
111 changes: 95 additions & 16 deletions cli/cmd/generate_azure.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"strconv"
"strings"
"time"

Expand All @@ -14,18 +15,24 @@ import (

var (
// Define question text here so they can be reused in testing
QuestionAzureEnableConfig = "Enable Azure configuration integration?"
QuestionAzureConfigName = "Specify custom configuration integration name: (optional)"
QuestionEnableActivityLog = "Enable Azure Activity Log Integration?"
QuestionActivityLogName = "Specify custom Activity Log integration name: (optional)"
QuestionAddAzureSubscriptionID = "Set Azure Subscription ID?"
QuestionAzureSubscriptionID = "Specify the Azure Subscription ID to be used to provision " +
QuestionAzureEnableConfig = "Enable Azure configuration integration?"
QuestionAzureConfigName = "Specify custom configuration integration name: (optional)"
QuestionEnableActivityLog = "Enable Azure Activity Log Integration?"
QuestionActivityLogName = "Specify custom Activity Log integration name: (optional)"
QuestionEnableEntraIdActivityLog = "Enable Azure Entra ID Activity Log Integration?"
QuestionEntraIdActivityLogName = "Specify custom EntraID Activity Log integration name: (optional)"
QuestionAddAzureSubscriptionID = "Set Azure Subscription ID?"
QuestionAzureSubscriptionID = "Specify the Azure Subscription ID to be used to provision " +
"Lacework resources: (optional)"

QuestionAzureAnotherAdvancedOpt = "Configure another advanced integration option"
QuestionAzureConfigAdvanced = "Configure advanced integration options?"
QuestionAzureCustomizeOutputLocation = "Provide the location for the output to be written:"

// EntraID Activity Log
QuestionEventHubLocation = "Specify Azure region where the event hub for logging will reside"
QuestionEventHubPartitionCount = "Specify the number of partitions in the event hub for logging"

// Active Directory
QuestionEnableAdIntegration = "Create Active Directory Integration?"
QuestionADApplicationPass = "Specify the password of an existing Active Directory application"
Expand Down Expand Up @@ -58,14 +65,15 @@ var (
AzureUserIntegrationNames = "Customize integration name(s)"
AzureAdvancedOptLocation = "Customize output location (optional)"
AzureRegionStorage = "Customize Azure region for Storage Account (optional)"
AzureEntraIdAdvancedOpt = "Configure Entra ID activity log integration advanced options"

GenerateAzureCommandState = &azure.GenerateAzureTfConfigurationArgs{}
GenerateAzureCommandExtraState = &AzureGenerateCommandExtraState{}
CachedAzureAssetIacParams = "iac-azure-generate-params"
CachedAzureAssetExtraState = "iac-azure-extra-state"

// List of valid Azure Storage locations
validStorageLocations = map[string]bool{
validAzureLocations = map[string]bool{
"East US": true,
"East US 2": true,
"South Central US": true,
Expand Down Expand Up @@ -183,6 +191,9 @@ the new cloud account. In interactive mode, this command will:
azure.WithStorageLocation(GenerateAzureCommandState.StorageLocation),
azure.WithActivityLogIntegrationName(GenerateAzureCommandState.ActivityLogIntegrationName),
azure.WithConfigIntegrationName(GenerateAzureCommandState.ConfigIntegrationName),
azure.WithEntraIdActivityLogIntegrationName(GenerateAzureCommandState.EntraIdIntegrationName),
azure.WithEventHubLocation(GenerateAzureCommandState.EventHubLocation),
azure.WithEventHubPartitionCount(GenerateAzureCommandState.EventHubPartitionCount),
}

// Check if AD Creation is required, need to set values for current integration
Expand Down Expand Up @@ -214,6 +225,7 @@ the new cloud account. In interactive mode, this command will:
data := azure.NewTerraform(
GenerateAzureCommandState.Config,
GenerateAzureCommandState.ActivityLog,
GenerateAzureCommandState.EntraIdActivityLog,
GenerateAzureCommandState.CreateAdIntegration,
mods...)

Expand Down Expand Up @@ -275,7 +287,7 @@ the new cloud account. In interactive mode, this command will:
if err != nil {
return errors.Wrap(err, "failed to load command flags")
}
if err := validateStorageLocation(storageLocation); storageLocation != "" && err != nil {
if err := validateAzureLocation(storageLocation); storageLocation != "" && err != nil {
return err
}

Expand Down Expand Up @@ -346,9 +358,9 @@ func (a *AzureGenerateCommandExtraState) writeCache() {
}
}

func validateStorageLocation(location string) error {
if !validStorageLocations[location] {
return errors.New("invalid storage location supplied")
func validateAzureLocation(location string) error {
if !validAzureLocations[location] {
return errors.New("invalid Azure region prvovided")
}
return nil
}
Expand All @@ -359,14 +371,26 @@ func initGenerateAzureTfCommandFlags() {
&GenerateAzureCommandState.ActivityLog,
"activity_log",
false,
"enable active log integration")
"enable activity log integration")

generateAzureTfCommand.PersistentFlags().StringVar(
&GenerateAzureCommandState.ActivityLogIntegrationName,
"activity_log_integration_name",
"",
"specify a custom activity log integration name")

generateAzureTfCommand.PersistentFlags().BoolVar(
&GenerateAzureCommandState.EntraIdActivityLog,
"entra_id_activity_log",
false,
"enable Entra ID activity log integration")

generateAzureTfCommand.PersistentFlags().StringVar(
&GenerateAzureCommandState.EntraIdIntegrationName,
"entra_id_activity_log_integration_name",
"",
"specify a custom Entra ID activity log integration name")

generateAzureTfCommand.PersistentFlags().BoolVar(
&GenerateAzureCommandState.Config,
"configuration",
Expand Down Expand Up @@ -409,6 +433,18 @@ func initGenerateAzureTfCommandFlags() {
false,
"use existing storage account")

generateAzureTfCommand.PersistentFlags().StringVar(
&GenerateAzureCommandState.EventHubLocation,
"event_hub_location",
"",
"specify the location where the Event Hub for logging will reside")

generateAzureTfCommand.PersistentFlags().IntVar(
&GenerateAzureCommandState.EventHubPartitionCount,
"event_hub_partition_count",
1,
"specify the number of partitions for the Event Hub")

generateAzureTfCommand.PersistentFlags().StringVar(
&GenerateAzureCommandState.StorageAccountName,
"storage_account_name",
Expand Down Expand Up @@ -492,6 +528,11 @@ func promptAzureIntegrationNameQuestions(config *azure.GenerateAzureTfConfigurat
Checks: []*bool{&config.ActivityLog},
Response: &config.ActivityLogIntegrationName,
},
{
Prompt: &survey.Input{Message: QuestionEntraIdActivityLogName, Default: config.EntraIdIntegrationName},
Checks: []*bool{&config.EntraIdActivityLog},
Response: &config.EntraIdIntegrationName,
},
}); err != nil {
return err
}
Expand Down Expand Up @@ -526,6 +567,31 @@ func promptAzureStorageAccountQuestions(config *azure.GenerateAzureTfConfigurati
return nil
}

func promptAzureEntraIdActivityLogQuestions(config *azure.GenerateAzureTfConfigurationArgs) error {

if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Input{Message: QuestionEventHubLocation, Default: config.EventHubLocation},
Required: true,
Response: &config.EventHubLocation,
},
}); err != nil {
return err
}

if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Input{Message: QuestionEventHubPartitionCount,
Default: strconv.Itoa(config.EventHubPartitionCount)},
Response: &config.EventHubPartitionCount,
},
}); err != nil {
return err
}

return nil
}

func promptAzureSubscriptionQuestions(config *azure.GenerateAzureTfConfigurationArgs) error {

if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
Expand Down Expand Up @@ -609,7 +675,7 @@ func promptCustomizeAzureOutputLocation(extraState *AzureGenerateCommandExtraSta
return nil
}

func promptCustomizeAzureLoggingRegion(config *azure.GenerateAzureTfConfigurationArgs) error {
func promptCustomizeAzureStorageLoggingRegion(config *azure.GenerateAzureTfConfigurationArgs) error {
var region string
if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Expand All @@ -619,7 +685,7 @@ func promptCustomizeAzureLoggingRegion(config *azure.GenerateAzureTfConfiguratio
}); err != nil {
return err
}
if err := validateStorageLocation(region); err != nil {
if err := validateAzureLocation(region); err != nil {
return err
}
config.StorageLocation = region
Expand Down Expand Up @@ -678,6 +744,11 @@ func askAdvancedAzureOptions(
options = append(options, AzureManagmentGroup)
}

// Only show Entra ID options in the case of Entra ID integration
if config.EntraIdActivityLog {
options = append(options, AzureEntraIdAdvancedOpt)
}

options = append(options, AzureAdvancedOptLocation, AzureAdvancedOptDone)
if err := SurveyQuestionInteractiveOnly(SurveyQuestionWithValidationArgs{
Prompt: &survey.Select{
Expand All @@ -699,6 +770,10 @@ func askAdvancedAzureOptions(
if err := promptAzureStorageAccountQuestions(config); err != nil {
return err
}
case AzureEntraIdAdvancedOpt:
if err := promptAzureEntraIdActivityLogQuestions(config); err != nil {
return err
}
case AzureSubscriptions:
if err := promptAzureSubscriptionQuestions(config); err != nil {
return err
Expand All @@ -712,7 +787,7 @@ func askAdvancedAzureOptions(
return err
}
case AzureRegionStorage:
if err := promptCustomizeAzureLoggingRegion(config); err != nil {
if err := promptCustomizeAzureStorageLoggingRegion(config); err != nil {
return err
}
case AzureAdvancedOptLocation:
Expand Down Expand Up @@ -784,12 +859,16 @@ func promptAzureGenerate(
Prompt: &survey.Confirm{Message: QuestionEnableAdIntegration, Default: config.CreateAdIntegration},
Response: &config.CreateAdIntegration,
},
{
Prompt: &survey.Confirm{Message: QuestionEnableEntraIdActivityLog, Default: config.EntraIdActivityLog},
Response: &config.EntraIdActivityLog,
},
}); err != nil {
return err
}

// Validate one of config or activity log was enabled; otherwise error out
if !config.Config && !config.ActivityLog {
if !config.Config && !config.ActivityLog && !config.EntraIdActivityLog {
return errors.New("must enable activity log or config")
}

Expand Down
12 changes: 6 additions & 6 deletions cli/cmd/generate_azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,20 @@ func TestMissingValidEntity(t *testing.T) {
}

func TestValidStorageLocations(t *testing.T) {
err := validateStorageLocation("East US")
err := validateAzureLocation("East US")
assert.Nil(t, err)
err = validateStorageLocation("Brazil Southeast")
err = validateAzureLocation("Brazil Southeast")
assert.Nil(t, err)

}

func TestInvalidStorageLocations(t *testing.T) {
err := validateStorageLocation("Mars")
err := validateAzureLocation("Mars")
assert.Error(t, err)
assert.Equal(t, "invalid storage location supplied", err.Error())
err = validateStorageLocation("Jupiter")
assert.Equal(t, "invalid Azure region prvovided", err.Error())
err = validateAzureLocation("Jupiter")
assert.Error(t, err)
assert.Equal(t, "invalid storage location supplied", err.Error())
assert.Equal(t, "invalid Azure region prvovided", err.Error())
}

func TestAzureGenerationCache(t *testing.T) {
Expand Down
44 changes: 24 additions & 20 deletions cli/docs/lacework_generate_cloud-account_azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,30 @@ lacework generate cloud-account azure [flags]
### Options

```
--activity_log enable active log integration
--activity_log_integration_name string specify a custom activity log integration name
--ad_create create new active directory integration (default true)
--ad_id string existing active directory application id
--ad_pass string existing active directory application password
--ad_pid string existing active directory application service principle id
--all_subscriptions subscription ids grant read access to ALL subscriptions within Tenant (overrides subscription ids)
--apply run terraform apply for the generated hcl
--configuration enable configuration integration
--configuration_name string specify a custom configuration integration name
--existing_storage use existing storage account
-h, --help help for azure
--location string specify azure region where storage account logging resides
--management_group management group level integration
--management_group_id string specify management group id. Required if mgmt_group provided
--output string location to write generated content (default is ~/lacework/azure)
--storage_account_name string specify storage account name
--storage_resource_group string specify storage resource group
--subscription_id string specify the Azure Subscription ID to be used to provision Lacework resources
--subscription_ids strings list of subscriptions to grant read access; format is id1,id2,id3
--activity_log enable activity log integration
--activity_log_integration_name string specify a custom activity log integration name
--ad_create create new active directory integration (default true)
--ad_id string existing active directory application id
--ad_pass string existing active directory application password
--ad_pid string existing active directory application service principle id
--all_subscriptions subscription ids grant read access to ALL subscriptions within Tenant (overrides subscription ids)
--apply run terraform apply for the generated hcl
--configuration enable configuration integration
--configuration_name string specify a custom configuration integration name
--entra_id_activity_log enable Entra ID activity log integration
--entra_id_activity_log_integration_name string specify a custom Entra ID activity log integration name
--event_hub_location string specify the location where the Event Hub for logging will reside
--event_hub_partition_count int specify the number of partitions for the Event Hub (default 1)
--existing_storage use existing storage account
-h, --help help for azure
--location string specify azure region where storage account logging resides
--management_group management group level integration
--management_group_id string specify management group id. Required if mgmt_group provided
--output string location to write generated content (default is ~/lacework/azure)
--storage_account_name string specify storage account name
--storage_resource_group string specify storage resource group
--subscription_id string specify the Azure Subscription ID to be used to provision Lacework resources
--subscription_ids strings list of subscriptions to grant read access; format is id1,id2,id3
```

### Options inherited from parent commands
Expand Down
Loading

0 comments on commit e5ecd21

Please sign in to comment.