Skip to content

Commit

Permalink
feat: CLI guided config for creating Azure AD integration
Browse files Browse the repository at this point in the history
  • Loading branch information
Manan-Bhatia-0 committed May 23, 2024
1 parent 8d50d31 commit 3b06f3f
Show file tree
Hide file tree
Showing 10 changed files with 464 additions and 50 deletions.
164 changes: 148 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,26 @@ 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
QuestionUseExistingEventHubNamespace = "Use an existing Event Hub Namespace?"
QuestionEventHubNamespaceName = "Specify existing Event Hub Namespace name"
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 +67,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 +193,11 @@ 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.WithExistingEventHubNamespace(GenerateAzureCommandState.ExistingEventHubNamespace),
azure.WithEventHubNamespaceName(GenerateAzureCommandState.EventHubNamespaceName),
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 +229,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 +291,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 +362,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 +375,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 +437,30 @@ func initGenerateAzureTfCommandFlags() {
false,
"use existing storage account")

generateAzureTfCommand.PersistentFlags().BoolVar(
&GenerateAzureCommandState.ExistingEventHubNamespace,
"existing_event_hub_namespace",
false,
"use existing Event Hub Namespace")

generateAzureTfCommand.PersistentFlags().StringVar(
&GenerateAzureCommandState.EventHubNamespaceName,
"event_hub_namespace",
"",
"specify the name of the Event Hub Namespace")

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 +544,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 +583,51 @@ func promptAzureStorageAccountQuestions(config *azure.GenerateAzureTfConfigurati
return nil
}

// Similar to the above, prompt for event hub questions starting by asking for existing event hub namespace, if answer is no, then ask for event hub location. If answer is yes, then ask for namespace name.
func promptAzureEntraIdActivityLogQuestions(config *azure.GenerateAzureTfConfigurationArgs) error {
if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Confirm{Message: QuestionUseExistingEventHubNamespace, Default: config.ExistingEventHubNamespace},
Response: &config.ExistingEventHubNamespace,
},
}); err != nil {
return err
}

if !config.ExistingEventHubNamespace {
if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Input{Message: QuestionEventHubLocation, Default: config.EventHubLocation},
Required: true,
Response: &config.EventHubLocation,
},
}); err != nil {
return err
}
} else {
if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Input{Message: QuestionEventHubNamespaceName, Default: config.EventHubNamespaceName},
Required: true,
Response: &config.EventHubNamespaceName,
},
}); 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 +711,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,13 +721,30 @@ 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
return nil
}

func promptCustomizeAzureEventHubLoggingRegion(config *azure.GenerateAzureTfConfigurationArgs) error {

Check failure on line 731 in cli/cmd/generate_azure.go

View workflow job for this annotation

GitHub Actions / run-linting-and-unit-tests

func `promptCustomizeAzureEventHubLoggingRegion` is unused (unused)
var region string
if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{
{
Prompt: &survey.Input{Message: QuestionEventHubLocation, Default: config.EventHubLocation},
Response: &region,
},
}); err != nil {
return err
}
if err := validateAzureLocation(region); err != nil {
return err
}
config.EventHubLocation = region
return nil
}

func askAzureSubscriptionID(config *azure.GenerateAzureTfConfigurationArgs) error {
var addSubscription bool

Expand Down Expand Up @@ -678,6 +797,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 +823,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 +840,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 +912,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
6 changes: 6 additions & 0 deletions integration/test_resources/help/generate_cloud-account_azure
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Flags:
--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 specify a custom Entra ID activity log integration name
--event_hub_location specify the location where the Event Hub for logging will reside
--event_hub_namespace specify the name of the Event Hub Namespace
--event_hub_partition_count specify the number of partitions for the Event Hub
--existing_event_hub_namespace use existing Event Hub Namespace
--existing_storage use existing storage account
-h, --help help for azure
--location string specify azure region where storage account logging resides
Expand Down
Loading

0 comments on commit 3b06f3f

Please sign in to comment.