From 73f8b0c83eaf6c67728f871cc3f4e4544c851ba7 Mon Sep 17 00:00:00 2001 From: djmctavish <87976925+djmctavish@users.noreply.github.com> Date: Thu, 24 Mar 2022 16:22:12 +0000 Subject: [PATCH] feat(cli/cmd): CLI survey code for azure - ALLY-394 (#720) * feat(cli/cmd): Initial CLI survey code for azure - ALLY-394 * feat(lwgenerate): Updates to remove string literals from module elements - ALLY-394 * feat(cli/cmd): Addition of integration tests - ALLY-394 * feat(cli/cmd): Updated integration tests for Azure terraform generation - ALLY-394 * feat(cli/cmd): fixed assert failure on integration test - ALLY-394 * feat(cli/cmd): Updated from review comments, updated integration tests - ALLY-394 * feat(cli/cmd): fix rebase issues,updated integration tests - ALLY-394 * feat(cli/cmd): Add azure help tests - ALLY-394 * feat(cli/cmd): Review comment updates, update of tests - ALLY-394 * feat(cli/cmd): Remove env setting for testing - ALLY-394 * feat(cli/cmd): Update to storage account name setting - ALLY-394 * feat(cli/cmd): Update cli flags to match format we have in GCP and AWS - ALLY-394 --- cli/cmd/generate.go | 18 + cli/cmd/generate_azure.go | 738 +++++++++++++++ cli/cmd/generate_azure_test.go | 111 +++ integration/azure_generation_test.go | 844 ++++++++++++++++++ .../help/cloud-account_iac-generate | 1 + .../help/cloud-account_iac-generate_azure | 53 ++ lwgenerate/azure/azure.go | 29 +- lwgenerate/azure/azure_test.go | 8 +- .../activity-log-with-all-subscriptions.tf | 6 +- .../activity-log-with-existing-storage.tf | 6 +- .../activity-log-with-list-subscriptions.tf | 6 +- .../test-data/activity-log-with-location.tf | 6 +- .../test-data/activity_log_with_config.tf | 12 +- .../test-data/activity_log_without_config.tf | 6 +- .../config-log-with-list-subscriptions.tf | 6 +- .../config-with-all-subscriptions.tf | 6 +- .../test-data/config-with-management-group.tf | 6 +- .../test-data/config-with-storage-account.tf | 6 +- .../test-data/config_without_activity_log.tf | 6 +- .../azure/test-data/renamed_activity_log.tf | 6 +- lwgenerate/azure/test-data/renamed_config.tf | 6 +- .../renamed_config_and_activity_log.tf | 12 +- 22 files changed, 1830 insertions(+), 68 deletions(-) create mode 100644 cli/cmd/generate_azure.go create mode 100644 cli/cmd/generate_azure_test.go create mode 100644 integration/azure_generation_test.go create mode 100644 integration/test_resources/help/cloud-account_iac-generate_azure diff --git a/cli/cmd/generate.go b/cli/cmd/generate.go index f45dcd743..44599de3f 100644 --- a/cli/cmd/generate.go +++ b/cli/cmd/generate.go @@ -56,6 +56,22 @@ func (gcp *GcpGenerateCommandExtraState) writeCache() { } } +type AzureGenerateCommandExtraState struct { + Output string + TerraformApply bool +} + +func (a *AzureGenerateCommandExtraState) isEmpty() bool { + return a.Output == "" && !a.TerraformApply +} + +// Flush current state of the struct to disk, provided it's not empty +func (a *AzureGenerateCommandExtraState) writeCache() { + if !a.isEmpty() { + cli.WriteAssetToCache(CachedAzureAssetExtraState, time.Now().Add(time.Hour*1), a) + } +} + var ( QuestionRunTfPlan = "Run Terraform plan now?" QuestionUsePreviousCache = "Previous IaC generation detected, load cached values?" @@ -76,10 +92,12 @@ func init() { // Add cloud specific command flags initGenerateAwsTfCommandFlags() initGenerateGcpTfCommandFlags() + initGenerateAzureTfCommandFlags() // add sub-commands to the iac-generate command generateTfCommand.AddCommand(generateAwsTfCommand) generateTfCommand.AddCommand(generateGcpTfCommand) + generateTfCommand.AddCommand(generateAzureTfCommand) } type SurveyQuestionWithValidationArgs struct { diff --git a/cli/cmd/generate_azure.go b/cli/cmd/generate_azure.go new file mode 100644 index 000000000..78e37fcc6 --- /dev/null +++ b/cli/cmd/generate_azure.go @@ -0,0 +1,738 @@ +package cmd + +import ( + "path/filepath" + "strings" + "time" + + "github.com/AlecAivazis/survey/v2" + "github.com/imdario/mergo" + "github.com/spf13/cobra" + + "github.com/lacework/go-sdk/lwgenerate/azure" + "github.com/pkg/errors" +) + +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)" + + QuestionAzureAnotherAdvancedOpt = "Configure another advanced integration option" + QuestionAzureConfigAdvanced = "Configure advanced integration options?" + QuestionAzureCustomizeOutputLocation = "Provide the location for the output to be written:" + + // Active Directory + QuestionEnableAdIntegration = "Create Active Directory Integration?" + QuestionADApplicationPass = "Specify the password of an existing Active Directory application" + QuestionADApplicationId = "Specify the ID of an existing Active Directory application" + QuestionADServicePrincpleId = "Specify the Service Principle ID of an existing Active Directory application" + + // Storage Account + QuestionUseExistingStorageAccount = "Use an existing Storage Account?" + QuestionAzureRegion = "Specify the Azure region to be used by Storage Account logging" + QuestionStorageAccountName = "Specify existing Storage Account name" + QuestionStorageAccountResourceGroup = "Specify existing Storage Account Resource Group" + + QuestionStorageLocation = "Specify Azure region where Storage Account for logging resides " + + // Subscriptions + QuestionEnableAllSubscriptions = "Enable all subscriptions?" + QuestionSubscriptionIds = "Specify list of subscription ids to enable logging" + + // Management Group + QuestionEnableManagementGroup = "Enable Management Group level Integration?" + QuestionManagementGroupId = "Specify Management Group ID" + + // Select options + AzureAdvancedOptDone = "Done" + AdvancedAdIntegration = "Configure Lacework integration with an existing Active Directory (optional)" + AzureExistingStorageAcount = "Configure Storage Account (optional)" + AzureSubscriptions = "Configure Subscriptions (optional)" + AzureManagmentGroup = "Configure Management Group (optional)" + AzureStorageGroup = "Configure Storage Group (optional)" + AzureUserIntegrationNames = "Customize integration name(s)" + AzureAdvancedOptLocation = "Customize output location (optional)" + AzureRegionStorage = "Customize Azure region for Storage Account (optional)" + + 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{ + "East US": true, + "East US 2": true, + "South Central US": true, + "West US 2": true, + "West US 3": true, + "Australia East": true, + "Southeast Asia": true, + "North Europe": true, + "Sweden Central": true, + "UK South": true, + "West Europe": true, + "Central US": true, + "North Central US": true, + "West US": true, + "South Africa North": true, + "Central India": true, + "East Asia": true, + "Japan East": true, + "Jio India West": true, + "Korea Central": true, + "Canada Central": true, + "France Central": true, + "Germany West Central": true, + "Norway East": true, + "Switzerland North": true, + "UAE North": true, + "Brazil South": true, + "Central US (Stage)": true, + "East US (Stage)": true, + "East US 2 (Stage)": true, + "North Central US (Stage)": true, + "South Central US (Stage)": true, + "West US (Stage)": true, + "West US 2 (Stage)": true, + "Asia": true, + "Asia Pacific": true, + "Australia": true, + "Brazil": true, + "Canada": true, + "Europe": true, + "France": true, + "Germany": true, + "Global": true, + "India": true, + "Japan": true, + "Korea": true, + "Norway": true, + "South Africa": true, + "Switzerland": true, + "United Arab Emirates": true, + "United Kingdom": true, + "United States": true, + "United States EUAP": true, + "East Asia (Stage)": true, + "Southeast Asia (Stage)": true, + "Central US EUAP": true, + "East US 2 EUAP": true, + "West Central US": true, + "South Africa West": true, + "Australia Central": true, + "Australia Central 2": true, + "Australia Southeast": true, + "Japan West": true, + "Jio India Central": true, + "Korea South": true, + "South India": true, + "West India": true, + "Canada East": true, + "France South": true, + "Germany North": true, + "Norway West": true, + "Switzerland West": true, + "UK West": true, + "UAE Central": true, + "Brazil Southeast": true, + } + + // Azure command used to generate TF code for azure + generateAzureTfCommand = &cobra.Command{ + Use: "azure", + Aliases: []string{"az"}, + Short: "Generate and/or execute Terraform code for Azure integration", + Long: `Use this command to generate Terraform code for deploying Lacework into new Azure environment. + +By default, this command will function interactively, prompting for the required information to setup the new cloud account. In interactive mode, this command will: + +* Prompt for the required information to setup the integration +* Generate new Terraform code using the inputs +* Optionally, run the generated Terraform code: + * If Terraform is already installed, the version will be confirmed suitable for use + * If Terraform is not installed, or the version installed is not suitable, a new version will be installed into a temporary location + * Once Terraform is detected or installed, Terraform plan will be executed + * The command will prompt with the outcome of the plan and allow to view more details or continue with Terraform apply + * If confirmed, Terraform apply will be run, completing the setup of the cloud account +`, + RunE: func(cmd *cobra.Command, args []string) error { + // Generate TF Code + cli.StartProgress("Generating Azure Terraform Code...") + + // Setup modifiers for NewTerraform constructor + mods := []azure.AzureTerraformModifier{ + azure.WithAllSubscriptions(GenerateAzureCommandState.AllSubscriptions), + azure.WithManagementGroup(GenerateAzureCommandState.ManagementGroup), + azure.WithExistingStorageAccount(GenerateAzureCommandState.ExistingStorageAccount), + azure.WithStorageAccountName(GenerateAzureCommandState.StorageAccountName), + azure.WithStorageLocation(GenerateAzureCommandState.StorageLocation), + azure.WithActivityLogIntegrationName(GenerateAzureCommandState.ActivityLogIntegrationName), + azure.WithConfigIntegrationName(GenerateAzureCommandState.ConfigIntegrationName), + } + + // Check if AD Creation is required, need to set values for current integration + if !GenerateAzureCommandState.CreateAdIntegration { + mods = append(mods, azure.WithAdApplicationId(GenerateAzureCommandState.AdApplicationId)) + mods = append(mods, azure.WithAdApplicationPassword(GenerateAzureCommandState.AdApplicationPassword)) + mods = append(mods, azure.WithAdServicePrincipalId(GenerateAzureCommandState.AdServicePrincipalId)) + } + + // Check subscriptions + if !GenerateAzureCommandState.AllSubscriptions { + if len(GenerateAzureCommandState.SubscriptionIds) > 0 { + mods = append(mods, azure.WithSubscriptionIds(GenerateAzureCommandState.SubscriptionIds)) + } + } + + // Check management groups + if GenerateAzureCommandState.ManagementGroup { + mods = append(mods, azure.WithManagementGroupId(GenerateAzureCommandState.ManagementGroupId)) + } + + // Check storage account + if GenerateAzureCommandState.ExistingStorageAccount { + mods = append(mods, azure.WithStorageAccountResourceGroup(GenerateAzureCommandState.StorageAccountResourceGroup)) + } + + // Create new struct + data := azure.NewTerraform( + GenerateAzureCommandState.Config, + GenerateAzureCommandState.ActivityLog, + GenerateAzureCommandState.CreateAdIntegration, + mods...) + + // Generate HCL for azure deployment + hcl, err := data.Generate() + cli.StopProgress() + + if err != nil { + return errors.Wrap(err, "failed to generate terraform code") + } + + // Write-out generated code to location specified + dirname, location, err := writeGeneratedCodeToLocation(cmd, hcl, "azure") + if err != nil { + return err + } + + // Prompt to execute, if the command line flag has not been set + if !GenerateAzureCommandExtraState.TerraformApply { + err = SurveyQuestionInteractiveOnly(SurveyQuestionWithValidationArgs{ + Prompt: &survey.Confirm{Default: GenerateAzureCommandExtraState.TerraformApply, Message: QuestionRunTfPlan}, + Response: &GenerateAzureCommandExtraState.TerraformApply, + }) + + if err != nil { + return errors.Wrap(err, "failed to prompt for terraform execution") + } + } + + // Execute + locationDir := filepath.Dir(location) + if GenerateAzureCommandExtraState.TerraformApply { + // Execution pre-run check + err := executionPreRunChecks(dirname, locationDir, "azure") + if err != nil { + return err + } + } + + // Output where code was generated + if !GenerateAzureCommandExtraState.TerraformApply { + cli.OutputHuman(provideGuidanceAfterExit(false, false, locationDir, "terraform")) + } + + return nil + }, + PreRunE: func(cmd *cobra.Command, _ []string) error { + + // Validate output location is OK if supplied + dirname, err := cmd.Flags().GetString("output") + if err != nil { + return errors.Wrap(err, "failed to load command flags") + } + if err := validateOutputLocation(dirname); err != nil { + return err + } + + // Validate Storage Location + storageLocation, err := cmd.Flags().GetString("location") + if err != nil { + return errors.Wrap(err, "failed to load command flags") + } + if err := validateStorageLocation(storageLocation); storageLocation != "" && err != nil { + return err + } + + // Load any cached inputs if interactive + if cli.InteractiveMode() { + cachedOptions := &azure.GenerateAzureTfConfigurationArgs{} + iacParamsExpired := cli.ReadCachedAsset(CachedAzureAssetIacParams, &cachedOptions) + if iacParamsExpired { + cli.Log.Debug("loaded previously set values for Azure iac generation") + } + + extraState := &AzureGenerateCommandExtraState{} + extraStateParamsExpired := cli.ReadCachedAsset(CachedAzureAssetExtraState, &extraState) + if extraStateParamsExpired { + cli.Log.Debug("loaded previously set values for Azure iac generation (extra state)") + } + + // Determine if previously cached options exists; prompt user if they'd like to continue + answer := false + if !iacParamsExpired || !extraStateParamsExpired { + if err := SurveyQuestionInteractiveOnly(SurveyQuestionWithValidationArgs{ + Prompt: &survey.Confirm{Message: QuestionUsePreviousCache, Default: false}, + Response: &answer, + }); err != nil { + return errors.Wrap(err, "failed to load saved options") + } + } + + // If the user decides NOT to use the previous values; we won't load them. However, every time the command runs + // we are going to write out new cached values, so if they run it - bail out - and run it again they'll get + // re-prompted. + if answer { + // Merge cached inputs to current options (current options win) + if err := mergo.Merge(GenerateAzureCommandState, cachedOptions); err != nil { + return errors.Wrap(err, "failed to load saved options") + } + if err := mergo.Merge(GenerateAzureCommandExtraState, extraState); err != nil { + return errors.Wrap(err, "failed to load saved options") + } + } + } + + // Collect and/or confirm parameters + err = promptAzureGenerate(GenerateAzureCommandState, GenerateAzureCommandExtraState) + if err != nil { + return errors.Wrap(err, "collecting/confirming parameters") + } + + return nil + }, + } +) + +func validateStorageLocation(location string) error { + if !validStorageLocations[location] { + return errors.New("invalid storage location supplied") + } + return nil +} + +func initGenerateAzureTfCommandFlags() { + // Azure sub-command flags + generateAzureTfCommand.PersistentFlags().BoolVar( + &GenerateAzureCommandState.ActivityLog, + "activity_log", + false, + "enable active log integration") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.ActivityLogIntegrationName, + "activity_log_integration_name", + "", + "specify a custom activity log integration name") + + generateAzureTfCommand.PersistentFlags().BoolVar( + &GenerateAzureCommandState.Config, + "configuration", + false, + "enable configuration integration") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.ConfigIntegrationName, + "configuration_name", + "", + "specify a custom configuration integration name") + + generateAzureTfCommand.PersistentFlags().BoolVar( + &GenerateAzureCommandState.CreateAdIntegration, + "ad_create", + true, + "create new active directory integration") + + generateAzureTfCommand.PersistentFlags().BoolVar( + &GenerateAzureCommandState.ManagementGroup, + "management_group", + false, + "management group level integration") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.ManagementGroupId, + "management_group_id", + "", + "specify management group id. Required if mgmt_group provided") + + generateAzureTfCommand.PersistentFlags().BoolVar( + &GenerateAzureCommandState.ExistingStorageAccount, + "existing_storage", + false, + "use existing storage account") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.StorageAccountName, + "storage_account_name", + "", + "specify storage account name") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.StorageAccountResourceGroup, + "storage_resource_group", + "", + "specify storage resource group") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.StorageLocation, + "location", + "", + "specify azure region where storage account logging resides") + + generateAzureTfCommand.PersistentFlags().BoolVar( + &GenerateAzureCommandState.AllSubscriptions, + "all_subscriptions", + false, + "grant read access to ALL subscriptions within Tenant (overrides `subscription ids`)") + + generateAzureTfCommand.PersistentFlags().StringSliceVar( + &GenerateAzureCommandState.SubscriptionIds, + "subscription_ids", + []string{}, + `list of subscriptions to grant read access; format is id1,id2,id3`) + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.AdApplicationPassword, + "ad_pass", + "", + "existing active directory application password") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.AdApplicationId, + "ad_id", + "", + "existing active directory application id") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandState.AdServicePrincipalId, + "ad_pid", + "", + "existing active directory application service principle id") + + generateAzureTfCommand.PersistentFlags().BoolVar( + &GenerateAzureCommandExtraState.TerraformApply, + "terraform-apply", + false, + "run terraform apply for the generated hcl") + + generateAzureTfCommand.PersistentFlags().StringVar( + &GenerateAzureCommandExtraState.Output, + "output", + "", + "location to write generated content", + ) +} + +func promptAzureIntegrationNameQuestions(config *azure.GenerateAzureTfConfigurationArgs) error { + + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Input{Message: QuestionAzureConfigName, Default: config.ConfigIntegrationName}, + Checks: []*bool{&config.Config}, + Response: &config.ConfigIntegrationName, + }, + { + Prompt: &survey.Input{Message: QuestionActivityLogName, Default: config.ActivityLogIntegrationName}, + Checks: []*bool{&config.ActivityLog}, + Response: &config.ActivityLogIntegrationName, + }, + }); err != nil { + return err + } + return nil +} + +func promptAzureStorageAccountQuestions(config *azure.GenerateAzureTfConfigurationArgs) error { + + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Confirm{Message: QuestionUseExistingStorageAccount, Default: config.ExistingStorageAccount}, + Response: &config.ExistingStorageAccount, + }, + { + Prompt: &survey.Input{Message: QuestionStorageAccountName, Default: config.StorageAccountName}, + Required: true, + Response: &config.StorageAccountName, + }, + { + Prompt: &survey.Input{Message: QuestionStorageAccountResourceGroup, Default: config.StorageAccountResourceGroup}, + Checks: []*bool{&config.ExistingStorageAccount}, + Required: true, + Response: &config.StorageAccountResourceGroup, + }, + }); err != nil { + return err + } + + return nil +} + +func promptAzureSubscriptionQuestions(config *azure.GenerateAzureTfConfigurationArgs) error { + + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Confirm{Message: QuestionEnableAllSubscriptions, Default: config.AllSubscriptions}, + Response: &config.AllSubscriptions, + }, + }); err != nil { + return err + } + var idList string + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Input{Message: QuestionSubscriptionIds, Default: strings.Join(config.SubscriptionIds, ",")}, + Checks: []*bool{allSubscriptionsDisabled(config)}, + Required: true, + Response: &idList, + }, + }); err != nil { + return err + } + config.SubscriptionIds = strings.Split(strings.ReplaceAll(idList, " ", ""), ",") + + return nil +} + +func promptAzureManagementGroupQuestions(config *azure.GenerateAzureTfConfigurationArgs) error { + + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Confirm{Message: QuestionEnableManagementGroup, Default: config.ManagementGroup}, + Response: &config.ManagementGroup, + }, + { + Prompt: &survey.Input{Message: QuestionManagementGroupId, Default: config.ManagementGroupId}, + Checks: []*bool{&config.ManagementGroup}, + Required: true, + Response: &config.ManagementGroupId, + }, + }); err != nil { + return err + } + return nil +} + +func promptAzureAdIntegrationQuestions(config *azure.GenerateAzureTfConfigurationArgs) error { + + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Input{Message: QuestionADApplicationPass, Default: config.AdApplicationPassword}, + Required: true, + Response: &config.AdApplicationPassword, + }, + { + Prompt: &survey.Input{Message: QuestionADApplicationId, Default: config.AdApplicationId}, + Required: true, + Response: &config.AdApplicationId, + }, + { + Prompt: &survey.Input{Message: QuestionADServicePrincpleId, Default: config.AdServicePrincipalId}, + Required: true, + Response: &config.AdServicePrincipalId, + }, + }); err != nil { + return err + } + return nil +} + +func promptCustomizeAzureOutputLocation(extraState *AzureGenerateCommandExtraState) error { + + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Input{Message: QuestionAzureCustomizeOutputLocation, Default: extraState.Output}, + Response: &extraState.Output, + }, + }); err != nil { + return err + } + + return nil +} + +func promptCustomizeAzureLoggingRegion(config *azure.GenerateAzureTfConfigurationArgs) error { + var region string + if err := SurveyMultipleQuestionWithValidation([]SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Input{Message: QuestionStorageLocation, Default: config.StorageLocation}, + Response: ®ion, + }, + }); err != nil { + return err + } + if err := validateStorageLocation(region); err != nil { + return err + } + config.StorageLocation = region + return nil +} + +func askAdvancedAzureOptions(config *azure.GenerateAzureTfConfigurationArgs, extraState *AzureGenerateCommandExtraState) error { + answer := "" + + // Prompt for options + for answer != AzureAdvancedOptDone { + + // Set the initial options + options := []string{AzureUserIntegrationNames, AzureSubscriptions} + // Only ask about Active Directory information if one was requested to be created + if !config.CreateAdIntegration { + options = append(options, AdvancedAdIntegration) + } + + // Only show Region Storage options in the case of Activity Log integration + if config.ActivityLog { + options = append(options, AzureRegionStorage) + options = append(options, AzureExistingStorageAcount) + } + + // Only show management options in the case of Config integration + if config.Config { + options = append(options, AzureManagmentGroup) + } + + options = append(options, AzureAdvancedOptLocation, AzureAdvancedOptDone) + if err := SurveyQuestionInteractiveOnly(SurveyQuestionWithValidationArgs{ + Prompt: &survey.Select{ + Message: "Which options would you like to enable?", + Options: options, + }, + Response: &answer, + }); err != nil { + return err + } + + // Based on response, prompt for actions + switch answer { + case AzureUserIntegrationNames: + if err := promptAzureIntegrationNameQuestions(config); err != nil { + return err + } + case AzureExistingStorageAcount: + if err := promptAzureStorageAccountQuestions(config); err != nil { + return err + } + case AzureSubscriptions: + if err := promptAzureSubscriptionQuestions(config); err != nil { + return err + } + case AzureManagmentGroup: + if err := promptAzureManagementGroupQuestions(config); err != nil { + return err + } + case AdvancedAdIntegration: + if err := promptAzureAdIntegrationQuestions(config); err != nil { + return err + } + case AzureRegionStorage: + if err := promptCustomizeAzureLoggingRegion(config); err != nil { + return err + } + case AzureAdvancedOptLocation: + if err := promptCustomizeAzureOutputLocation(extraState); err != nil { + return err + } + return nil + } + + // Re-prompt if not done + innerAskAgain := true + if answer == AzureAdvancedOptDone { + innerAskAgain = false + } + + if err := SurveyQuestionInteractiveOnly(SurveyQuestionWithValidationArgs{ + Checks: []*bool{&innerAskAgain}, + Prompt: &survey.Confirm{Message: QuestionAzureAnotherAdvancedOpt, Default: false}, + Response: &innerAskAgain, + }); err != nil { + return err + } + + if !innerAskAgain { + answer = AzureAdvancedOptDone + } + } + + return nil +} + +func azureConfigIsEmpty(config *azure.GenerateAzureTfConfigurationArgs) bool { + return !config.Config && !config.ActivityLog +} + +func allSubscriptionsDisabled(config *azure.GenerateAzureTfConfigurationArgs) *bool { + allSubscriptionsDisabled := !config.AllSubscriptions + return &allSubscriptionsDisabled +} + +func writeAzureGenerationArgsCache(config *azure.GenerateAzureTfConfigurationArgs) { + if !azureConfigIsEmpty(config) { + cli.WriteAssetToCache(CachedAzureAssetIacParams, time.Now().Add(time.Hour*1), config) + } +} + +// entry point for launching a survey to build out the required Azure generation parameters +func promptAzureGenerate(config *azure.GenerateAzureTfConfigurationArgs, extraState *AzureGenerateCommandExtraState) error { + + // Cache for later use if generation is abandoned and in interactive mode + if cli.InteractiveMode() { + defer writeAzureGenerationArgsCache(config) + defer extraState.writeCache() + } + // These are the core questions that should be asked. + if err := SurveyMultipleQuestionWithValidation( + []SurveyQuestionWithValidationArgs{ + { + Prompt: &survey.Confirm{Message: QuestionAzureEnableConfig, Default: false}, + Response: &config.Config, + }, + { + Prompt: &survey.Confirm{Message: QuestionEnableActivityLog, Default: false}, + Response: &config.ActivityLog, + }, + { + Prompt: &survey.Confirm{Message: QuestionEnableAdIntegration, Default: false}, + Response: &config.CreateAdIntegration, + }, + }); err != nil { + return err + } + + // Validate one of config or activity log was enabled; otherwise error out + if !config.Config && !config.ActivityLog { + return errors.New("must enable activity log or config") + } + + // Find out if the customer wants to specify more advanced features + askAdvanced := false + if err := SurveyQuestionInteractiveOnly(SurveyQuestionWithValidationArgs{ + Prompt: &survey.Confirm{Message: QuestionAzureConfigAdvanced, Default: askAdvanced}, + Response: &askAdvanced, + }); err != nil { + return err + } + + // Keep prompting for advanced options until the say done + if askAdvanced { + if err := askAdvancedAzureOptions(config, extraState); err != nil { + return err + } + } + + return nil +} diff --git a/cli/cmd/generate_azure_test.go b/cli/cmd/generate_azure_test.go new file mode 100644 index 000000000..274f0c583 --- /dev/null +++ b/cli/cmd/generate_azure_test.go @@ -0,0 +1,111 @@ +package cmd + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/lacework/go-sdk/lwgenerate/azure" + "github.com/stretchr/testify/assert" +) + +func toggleAzureNonInteractive() { + cli.noCache = !cli.noCache + cli.nonInteractive = !cli.nonInteractive +} + +func TestGenerationActivityLogWithConfig(t *testing.T) { + toggleAzureNonInteractive() + defer toggleAzureNonInteractive() + + data := azure.GenerateAzureTfConfigurationArgs{} + data.Config = true + data.ActivityLog = true + data.CreateAdIntegration = true + err := promptAzureGenerate(&data, &AzureGenerateCommandExtraState{Output: "/tmp"}) + assert.Nil(t, err) + +} + +func TestMissingValidEntity(t *testing.T) { + toggleNonInteractive() + defer toggleNonInteractive() + + data := azure.GenerateAzureTfConfigurationArgs{} + data.Config = false + data.ActivityLog = false + + err := promptAzureGenerate(&data, &AzureGenerateCommandExtraState{Output: "/tmp"}) + assert.Error(t, err) + assert.Equal(t, "must enable activity log or config", err.Error()) +} + +func TestValidStorageLocations(t *testing.T) { + err := validateStorageLocation("East US") + assert.Nil(t, err) + err = validateStorageLocation("Brazil Southeast") + assert.Nil(t, err) + +} + +func TestInvalidStorageLocations(t *testing.T) { + err := validateStorageLocation("Mars") + assert.Error(t, err) + assert.Equal(t, "invalid storage location supplied", err.Error()) + err = validateStorageLocation("Jupiter") + assert.Error(t, err) + assert.Equal(t, "invalid storage location supplied", err.Error()) +} + +func TestAzureGenerationCache(t *testing.T) { + t.Run("extra state shouldn't be written if empty", func(t *testing.T) { + dir, err := ioutil.TempDir("", "lacework-cli-cache") + if err != nil { + panic(err) + } + defer os.RemoveAll(dir) + cli.InitCache(dir) + + extraState := &AzureGenerateCommandExtraState{} + extraState.writeCache() + assert.NoFileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAzureAssetExtraState))) + }) + t.Run("extra state should be written if not empty", func(t *testing.T) { + dir, err := ioutil.TempDir("", "lacework-cli-cache") + if err != nil { + panic(err) + } + defer os.RemoveAll(dir) + cli.InitCache(dir) + + extraState := AzureGenerateCommandExtraState{Output: "/tmp"} + extraState.writeCache() + assert.FileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAzureAssetExtraState))) + }) + t.Run("iac params should not be cached when empty", func(t *testing.T) { + dir, err := ioutil.TempDir("", "lacework-cli-cache") + if err != nil { + panic(err) + } + defer os.RemoveAll(dir) + cli.InitCache(dir) + + args := azure.GenerateAzureTfConfigurationArgs{} + writeAzureGenerationArgsCache(&args) + assert.NoFileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAzureAssetIacParams))) + }) + t.Run("iac params should be cached when not empty", func(t *testing.T) { + dir, err := ioutil.TempDir("", "lacework-cli-cache") + if err != nil { + panic(err) + } + defer os.RemoveAll(dir) + cli.InitCache(dir) + + args := azure.GenerateAzureTfConfigurationArgs{ActivityLog: true} + writeAzureGenerationArgsCache(&args) + assert.FileExists(t, filepath.FromSlash(fmt.Sprintf("%s/cache/standalone/%s", dir, CachedAzureAssetIacParams))) + }) +} diff --git a/integration/azure_generation_test.go b/integration/azure_generation_test.go new file mode 100644 index 000000000..d4ca74ac7 --- /dev/null +++ b/integration/azure_generation_test.go @@ -0,0 +1,844 @@ +//go:build !windows && generation + +package integration + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/Netflix/go-expect" + "github.com/lacework/go-sdk/cli/cmd" + "github.com/lacework/go-sdk/lwgenerate/azure" + "github.com/stretchr/testify/assert" +) + +func expectAzureString(c *expect.Console, str string, runError *error) { + out, err := c.Expect(expect.WithTimeout(time.Second), expect.String(str)) + if err != nil { + fmt.Println(out) // To see the errored line, you can enable this and update _ above to out + *runError = err + } +} + +// Test failing due to no selection +func TestGenerationAzureErrorOnNoSelection(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var runError error + + // Run CLI + runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("n") + expectAzureString(c, "ERROR collecting/confirming parameters: must enable activity log or config", &runError) + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI errored properly + assert.Nil(t, runError) +} + +// Test barebones generation with no customization +func TestGenerationAzureSimple(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, true, true).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test customized output location +func TestGenerationAzureCustomizedOutputLocation(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + + // Tempdir for test + dir, err := ioutil.TempDir("", "lacework-cli") + if err != nil { + panic(err) + } + defer os.RemoveAll(dir) + + // Run CLI + runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.Send("\x1B[B") + c.Send("\x1B[B") + c.Send("\x1B[B") + c.Send("\x1B[B") + c.SendLine("\x1B[B") + expectAzureString(c, cmd.QuestionAzureCustomizeOutputLocation, &runError) + c.SendLine(dir) + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Get result + result, _ := ioutil.ReadFile(filepath.FromSlash(fmt.Sprintf("%s/main.tf", dir))) + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, true, true).Generate() + assert.Equal(t, buildTf, string(result)) +} + +// Test config only generation +func TestGenerationAzureConfigOnly(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, false, true).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test activity log only generation +func TestGenerationAzureActivityLogOnly(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(false, true, true).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test no AD creation generation +func TestGenerationAzureNoADEnabled(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + var pass string = "super-secret-password" + var principalId string = "test-prinicpal-id" + var applicationId string = "test-application-id" + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptLocation, &runError) + c.Send("\x1B[B") + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionADApplicationPass, &runError) + c.SendLine(pass) + expectAzureString(c, cmd.QuestionADApplicationId, &runError) + c.SendLine(applicationId) + expectAzureString(c, cmd.QuestionADServicePrincpleId, &runError) + c.SendLine(principalId) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, true, false, + azure.WithAdApplicationPassword(pass), + azure.WithAdServicePrincipalId(principalId), + azure.WithAdApplicationId(applicationId), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generation with config name customization +func TestGenerationAzureNamedConfig(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + var configName string = "Test-Config-Rename" + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.SendLine("") + + expectAzureString(c, cmd.QuestionAzureConfigName, &runError) + c.SendLine(configName) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, false, true, + azure.WithConfigIntegrationName(configName), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generation with activity log customization +func TestGenerationAzureNamedActivityLog(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + var activityName string = "Test Activity Log Rename" + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.SendLine("") + + expectAzureString(c, cmd.QuestionActivityLogName, &runError) + c.SendLine(activityName) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(false, true, true, + azure.WithActivityLogIntegrationName(activityName)).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test Bailing out of Advanced Options +func TestGenerationAzureAdvancedOptsDone(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.Send("\x1B[B") + c.Send("\x1B[B") + c.Send("\x1B[B") + c.Send("\x1B[B") + c.Send("\x1B[B") + c.SendLine("\x1B[B") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, true, true).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test existing main.tf prompt +func TestGenerationAzureWithExistingTerraform(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + //var final string + var runError error + + // Tempdir for test + dir, err := ioutil.TempDir("", "lacework-cli") + if err != nil { + panic(err) + } + //defer os.RemoveAll(dir) + + // Create fake main.tf + if err := os.WriteFile(filepath.FromSlash(fmt.Sprintf("%s/main.tf", dir)), []byte{}, 0644); err != nil { + panic(err) + } + + // Run CLI + runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.Send("\x1B[B") + c.Send("\x1B[B") + c.Send("\x1B[B") + c.Send("\x1B[B") + c.SendLine("\x1B[B") + expectAzureString(c, cmd.QuestionAzureCustomizeOutputLocation, &runError) + c.SendLine(dir) + expectAzureString(c, fmt.Sprintf("%s/main.tf already exists, overwrite?", dir), &runError) + c.SendLine("n") + + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + _, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + data, err := os.ReadFile(fmt.Sprintf("%s/main.tf", dir)) + if err != nil { + panic(err) + } + + assert.Empty(t, data) + assert.Nil(t, runError) +} + +// Test generate Config with all subscriptions +func TestGenerationAzureConfigAllSubs(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionEnableAllSubscriptions, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, false, true, + azure.WithAllSubscriptions(true), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generate Config with management group +func TestGenerationAzureConfigMgmntGroup(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + var mgmtGrpId string = "test-management-group-1" + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.Send("\x1B[B") + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionEnableManagementGroup, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionManagementGroupId, &runError) + c.SendLine(mgmtGrpId) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, false, true, + azure.WithManagementGroup(true), + azure.WithManagementGroupId(mgmtGrpId), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generate Config log with subscription ids +func TestGenerationAzureConfigSubs(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + testIds := []string{"test-id-1", "test-id-2", "test-id-3"} + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionEnableAllSubscriptions, &runError) + c.SendLine("n") + + expectAzureString(c, cmd.QuestionSubscriptionIds, &runError) + c.SendLine(strings.Join(testIds[:], ",")) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(true, false, true, + azure.WithSubscriptionIds(testIds), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generate Activity log with subscription ids +func TestGenerationAzureActivityLogSubs(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + testIds := []string{"test-id-1", "test-id-2", "test-id-3"} + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionEnableAllSubscriptions, &runError) + c.SendLine("n") + + expectAzureString(c, cmd.QuestionSubscriptionIds, &runError) + c.SendLine(strings.Join(testIds[:], ",")) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(false, true, true, + azure.WithSubscriptionIds(testIds), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generate ActivityLog with exising storage account +func TestGenerationAzureActivityLogStorageAccount(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + var storageAccountName string = "test-storage-account-name" + var storageResourceGrp string = "test-storage-account-resource-group" + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.Send("\x1B[B") + c.Send("\x1B[B") + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionUseExistingStorageAccount, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.QuestionStorageAccountName, &runError) + c.SendLine(storageAccountName) + expectAzureString(c, cmd.QuestionStorageAccountResourceGroup, &runError) + c.SendLine(storageResourceGrp) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(false, true, true, + azure.WithExistingStorageAccount(true), + azure.WithStorageAccountName(storageAccountName), + azure.WithStorageAccountResourceGroup(storageResourceGrp), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generate ActivityLog with all subscriptions +func TestGenerationAzureActivityLogAllSubs(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionEnableAllSubscriptions, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(false, true, true, + azure.WithAllSubscriptions(true), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +// Test generate user supplied location for logging +func TestGenerationAzureActivityLogLocation(t *testing.T) { + os.Setenv("LW_NOCACHE", "true") + defer os.Setenv("LW_NOCACHE", "") + var final string + var runError error + var region string = "West US 2" + + // Run CLI + tfResult := runGenerateAzureTest(t, + func(c *expect.Console) { + expectAzureString(c, cmd.QuestionAzureEnableConfig, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionEnableActivityLog, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionEnableAdIntegration, &runError) + c.SendLine("y") + expectAzureString(c, cmd.QuestionAzureConfigAdvanced, &runError) + c.SendLine("y") + + expectAzureString(c, cmd.AzureAdvancedOptDone, &runError) + c.Send("\x1B[B") + c.SendLine("\x1B[B") + + expectAzureString(c, cmd.QuestionStorageLocation, &runError) + c.SendLine(region) + + expectAzureString(c, cmd.QuestionAzureAnotherAdvancedOpt, &runError) + c.SendLine("n") + expectAzureString(c, cmd.QuestionRunTfPlan, &runError) + c.SendLine("n") + final, _ = c.ExpectEOF() + }, + "cloud", + "iac", + "az", + ) + + // Ensure CLI ran correctly + assert.Nil(t, runError) + assert.Contains(t, final, "Terraform code saved in") + + // Create the TF directly with lwgenerate and validate same result via CLI + buildTf, _ := azure.NewTerraform(false, true, true, + azure.WithStorageLocation(region), + ).Generate() + assert.Equal(t, buildTf, tfResult) +} + +func runGenerateAzureTest(t *testing.T, conditions func(*expect.Console), args ...string) string { + // create a temporal directory where we will check that the + // configuration file is deployed (.lacework.toml) + dir := createDummyTOMLConfig() + homeCache := os.Getenv("HOME") + os.Setenv("HOME", dir) + defer os.Setenv("HOME", homeCache) + defer os.RemoveAll(dir) + + runFakeTerminalTestFromDir(t, dir, conditions, args...) + out, err := ioutil.ReadFile(filepath.FromSlash(fmt.Sprintf("%s/lacework/azure/main.tf", dir))) + if err != nil { + // Assume couldn't be found + return "" + } + + return string(out) +} diff --git a/integration/test_resources/help/cloud-account_iac-generate b/integration/test_resources/help/cloud-account_iac-generate index ca2878313..b95353b91 100644 --- a/integration/test_resources/help/cloud-account_iac-generate +++ b/integration/test_resources/help/cloud-account_iac-generate @@ -8,6 +8,7 @@ Aliases: Available Commands: aws Generate and/or execute Terraform code for AWS integration + azure Generate and/or execute Terraform code for Azure integration gcp Generate and/or execute Terraform code for GCP integration Flags: diff --git a/integration/test_resources/help/cloud-account_iac-generate_azure b/integration/test_resources/help/cloud-account_iac-generate_azure new file mode 100644 index 000000000..595246174 --- /dev/null +++ b/integration/test_resources/help/cloud-account_iac-generate_azure @@ -0,0 +1,53 @@ +Use this command to generate Terraform code for deploying Lacework into new Azure environment. + +By default, this command will function interactively, prompting for the required information to setup the new cloud account. In interactive mode, this command will: + +* Prompt for the required information to setup the integration +* Generate new Terraform code using the inputs +* Optionally, run the generated Terraform code: + * If Terraform is already installed, the version will be confirmed suitable for use + * If Terraform is not installed, or the version installed is not suitable, a new version will be installed into a temporary location + * Once Terraform is detected or installed, Terraform plan will be executed + * The command will prompt with the outcome of the plan and allow to view more details or continue with Terraform apply + * If confirmed, Terraform apply will be run, completing the setup of the cloud account + +Usage: + lacework cloud-account iac-generate azure [flags] + +Aliases: + azure, az + +Flags: + --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) + --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 + --storage_account_name string specify storage account name + --storage_resource_group string specify storage resource group + --subscription_ids strings list of subscriptions to grant read access; format is id1,id2,id3 + --terraform-apply run terraform apply for the generated hcl + +Global Flags: + -a, --account string account subdomain of URL (i.e. .lacework.net) + -k, --api_key string access key id + -s, --api_secret string secret access key + --api_token string access token (replaces the use of api_key and api_secret) + --debug turn on debug logging + --json switch commands output from human-readable to json format + --nocache turn off caching + --nocolor turn off colors + --noninteractive turn off interactive mode (disable spinners, prompts, etc.) + --organization access organization level data sets (org admins only) + -p, --profile string switch between profiles configured at ~/.lacework.toml + --subaccount string sub-account name inside your organization (org admins only) diff --git a/lwgenerate/azure/azure.go b/lwgenerate/azure/azure.go index 69032759f..2c643f52a 100644 --- a/lwgenerate/azure/azure.go +++ b/lwgenerate/azure/azure.go @@ -103,20 +103,13 @@ func WithConfigIntegrationName(name string) AzureTerraformModifier { } } -// WithAuditLogIntegrationName Set the Config Integration name to be displayed on the Lacework UI -func WithAuditLogIntegrationName(name string) AzureTerraformModifier { +// WithActivityLogIntegrationName Set the Activity Log Integration name to be displayed on the Lacework UI +func WithActivityLogIntegrationName(name string) AzureTerraformModifier { return func(c *GenerateAzureTfConfigurationArgs) { c.ActivityLogIntegrationName = name } } -// WithCreateAdIntegration Set the Config Integration name to be displayed on the Lacework UI -func WithCreateAdIntegration(enableAdIntegration bool) AzureTerraformModifier { - return func(c *GenerateAzureTfConfigurationArgs) { - c.CreateAdIntegration = enableAdIntegration - } -} - // WithAdApplicationId Set Active Directory application id func WithAdApplicationId(AdApplicationId string) AzureTerraformModifier { return func(c *GenerateAzureTfConfigurationArgs) { @@ -340,9 +333,9 @@ func createConfig(args *GenerateAzureTfConfigurationArgs) ([]*hclwrite.Block, er // Check if we have created an Active Directory app if args.CreateAdIntegration { attributes["use_existing_ad_application"] = true - attributes["application_id"] = "module.az_ad_application.application_id" - attributes["application_password"] = "module.az_ad_application.application_password" - attributes["service_principal_id"] = "module.az_ad_application.service_principal_id" + attributes["application_id"] = lwgenerate.CreateSimpleTraversal([]string{"module", "az_ad_application", "application_id"}) + attributes["application_password"] = lwgenerate.CreateSimpleTraversal([]string{"module", "az_ad_application", "application_password"}) + attributes["service_principal_id"] = lwgenerate.CreateSimpleTraversal([]string{"module", "az_ad_application", "service_principal_id"}) } else { attributes["use_existing_ad_application"] = true attributes["application_id"] = args.AdApplicationId @@ -404,9 +397,9 @@ func createActivityLog(args *GenerateAzureTfConfigurationArgs) ([]*hclwrite.Bloc // Check if we have created an Active Directory integration if args.CreateAdIntegration { attributes["use_existing_ad_application"] = true - attributes["application_id"] = "module.az_ad_application.application_id" - attributes["application_password"] = "module.az_ad_application.application_password" - attributes["service_principal_id"] = "module.az_ad_application.service_principal_id" + attributes["application_id"] = lwgenerate.CreateSimpleTraversal([]string{"module", "az_ad_application", "application_id"}) + attributes["application_password"] = lwgenerate.CreateSimpleTraversal([]string{"module", "az_ad_application", "application_password"}) + attributes["service_principal_id"] = lwgenerate.CreateSimpleTraversal([]string{"module", "az_ad_application", "service_principal_id"}) } else { attributes["use_existing_ad_application"] = true attributes["application_id"] = args.AdApplicationId @@ -424,10 +417,14 @@ func createActivityLog(args *GenerateAzureTfConfigurationArgs) ([]*hclwrite.Bloc attributes["all_subscriptions"] = args.AllSubscriptions } + // Set storage account name, if set + if args.StorageAccountName != "" { + attributes["storage_account_name"] = args.StorageAccountName + } + // Set storage info if existing storage flag is set if args.ExistingStorageAccount { attributes["use_existing_storage_account"] = args.ExistingStorageAccount - attributes["storage_account_name"] = args.StorageAccountName attributes["storage_account_resource_group"] = args.StorageAccountResourceGroup } diff --git a/lwgenerate/azure/azure_test.go b/lwgenerate/azure/azure_test.go index 7d54d3543..863808e38 100644 --- a/lwgenerate/azure/azure_test.go +++ b/lwgenerate/azure/azure_test.go @@ -65,7 +65,7 @@ func TestGenerationRenamedActivityLog(t *testing.T) { RenamedActivityLog, fileErr := getFileContent("test-data/renamed_activity_log.tf") assert.Nil(t, fileErr) hcl, err := azure.NewTerraform(false, true, true, - azure.WithAuditLogIntegrationName("Test Activity Log Rename"), + azure.WithActivityLogIntegrationName("Test Activity Log Rename"), ).Generate() assert.Nil(t, err) assert.NotNil(t, hcl) @@ -77,7 +77,7 @@ func TestGenerationRenamedConfigAndActivityLog(t *testing.T) { assert.Nil(t, fileErr) hcl, err := azure.NewTerraform(true, true, true, azure.WithConfigIntegrationName("Test Config Rename"), - azure.WithAuditLogIntegrationName("Test Activity Log Rename"), + azure.WithActivityLogIntegrationName("Test Activity Log Rename"), ).Generate() assert.Nil(t, err) assert.NotNil(t, hcl) @@ -87,7 +87,7 @@ func TestGenerationRenamedConfigAndActivityLog(t *testing.T) { func TestGenerationNoActiveDirectorySettings(t *testing.T) { hcl, err := azure.NewTerraform(true, true, false, azure.WithConfigIntegrationName("Test Config Rename"), - azure.WithAuditLogIntegrationName("Test Activity Log Rename"), + azure.WithActivityLogIntegrationName("Test Activity Log Rename"), ).Generate() assert.True(t, strings.Contains(errors.Unwrap(err).Error(), "invalid inputs")) assert.Empty(t, hcl) @@ -98,7 +98,7 @@ func TestGenerationCustomActiveDirectory(t *testing.T) { assert.Nil(t, fileErr) hcl, err := azure.NewTerraform(true, true, false, azure.WithConfigIntegrationName("Test Config Rename"), - azure.WithAuditLogIntegrationName("Test Activity Log Rename"), + azure.WithActivityLogIntegrationName("Test Activity Log Rename"), azure.WithAdApplicationPassword("AD-Test-Password"), azure.WithAdServicePrincipalId("AD-Test-Principal-ID"), azure.WithAdApplicationId("AD-Test-Application-ID"), diff --git a/lwgenerate/azure/test-data/activity-log-with-all-subscriptions.tf b/lwgenerate/azure/test-data/activity-log-with-all-subscriptions.tf index 68b86dc62..752b3c05b 100644 --- a/lwgenerate/azure/test-data/activity-log-with-all-subscriptions.tf +++ b/lwgenerate/azure/test-data/activity-log-with-all-subscriptions.tf @@ -28,8 +28,8 @@ module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" all_subscriptions = true - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/activity-log-with-existing-storage.tf b/lwgenerate/azure/test-data/activity-log-with-existing-storage.tf index 8add75d13..5362bc697 100644 --- a/lwgenerate/azure/test-data/activity-log-with-existing-storage.tf +++ b/lwgenerate/azure/test-data/activity-log-with-existing-storage.tf @@ -27,9 +27,9 @@ module "az_ad_application" { module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id storage_account_name = "Test-Storage-Account-Name" storage_account_resource_group = "Test-Storage-Account-Resource-Group" use_existing_ad_application = true diff --git a/lwgenerate/azure/test-data/activity-log-with-list-subscriptions.tf b/lwgenerate/azure/test-data/activity-log-with-list-subscriptions.tf index 1fbeabb8e..482dbcb75 100644 --- a/lwgenerate/azure/test-data/activity-log-with-list-subscriptions.tf +++ b/lwgenerate/azure/test-data/activity-log-with-list-subscriptions.tf @@ -27,9 +27,9 @@ module "az_ad_application" { module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id subscription_ids = ["test-id-1", "test-id-2", "test-id-3"] use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/activity-log-with-location.tf b/lwgenerate/azure/test-data/activity-log-with-location.tf index 044b56217..702e0915c 100644 --- a/lwgenerate/azure/test-data/activity-log-with-location.tf +++ b/lwgenerate/azure/test-data/activity-log-with-location.tf @@ -27,9 +27,9 @@ module "az_ad_application" { module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password location = "West US 2" - service_principal_id = "module.az_ad_application.service_principal_id" + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/activity_log_with_config.tf b/lwgenerate/azure/test-data/activity_log_with_config.tf index 43ad4a545..ca8be1563 100644 --- a/lwgenerate/azure/test-data/activity_log_with_config.tf +++ b/lwgenerate/azure/test-data/activity_log_with_config.tf @@ -27,17 +27,17 @@ module "az_ad_application" { module "az_config" { source = "lacework/config/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/activity_log_without_config.tf b/lwgenerate/azure/test-data/activity_log_without_config.tf index 08f350a94..0d3e27e5a 100644 --- a/lwgenerate/azure/test-data/activity_log_without_config.tf +++ b/lwgenerate/azure/test-data/activity_log_without_config.tf @@ -27,8 +27,8 @@ module "az_ad_application" { module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/config-log-with-list-subscriptions.tf b/lwgenerate/azure/test-data/config-log-with-list-subscriptions.tf index 4f2c26bf8..4bf3eec81 100644 --- a/lwgenerate/azure/test-data/config-log-with-list-subscriptions.tf +++ b/lwgenerate/azure/test-data/config-log-with-list-subscriptions.tf @@ -27,9 +27,9 @@ module "az_ad_application" { module "az_config" { source = "lacework/config/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id subscription_ids = ["test-id-1", "test-id-2", "test-id-3"] use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/config-with-all-subscriptions.tf b/lwgenerate/azure/test-data/config-with-all-subscriptions.tf index d9ce9110c..837cf6528 100644 --- a/lwgenerate/azure/test-data/config-with-all-subscriptions.tf +++ b/lwgenerate/azure/test-data/config-with-all-subscriptions.tf @@ -28,8 +28,8 @@ module "az_config" { source = "lacework/config/azure" version = "~> 1.0" all_subscriptions = true - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/config-with-management-group.tf b/lwgenerate/azure/test-data/config-with-management-group.tf index 8bf824cc0..f42ea00cf 100644 --- a/lwgenerate/azure/test-data/config-with-management-group.tf +++ b/lwgenerate/azure/test-data/config-with-management-group.tf @@ -27,10 +27,10 @@ module "az_ad_application" { module "az_config" { source = "lacework/config/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password management_group_id = "test-management-group-1" - service_principal_id = "module.az_ad_application.service_principal_id" + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true use_management_group = true } diff --git a/lwgenerate/azure/test-data/config-with-storage-account.tf b/lwgenerate/azure/test-data/config-with-storage-account.tf index b8ad03efc..b93989668 100644 --- a/lwgenerate/azure/test-data/config-with-storage-account.tf +++ b/lwgenerate/azure/test-data/config-with-storage-account.tf @@ -27,9 +27,9 @@ module "az_ad_application" { module "az_config" { source = "lacework/config/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id storage_account_name = "test-storage-account-name" storage_account_resource_group = "test-storage-account-resource-group" use_existing_ad_application = true diff --git a/lwgenerate/azure/test-data/config_without_activity_log.tf b/lwgenerate/azure/test-data/config_without_activity_log.tf index 524202460..06eff70dd 100644 --- a/lwgenerate/azure/test-data/config_without_activity_log.tf +++ b/lwgenerate/azure/test-data/config_without_activity_log.tf @@ -27,8 +27,8 @@ module "az_ad_application" { module "az_config" { source = "lacework/config/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" - service_principal_id = "module.az_ad_application.service_principal_id" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/renamed_activity_log.tf b/lwgenerate/azure/test-data/renamed_activity_log.tf index 1787a7d86..49551d72c 100644 --- a/lwgenerate/azure/test-data/renamed_activity_log.tf +++ b/lwgenerate/azure/test-data/renamed_activity_log.tf @@ -27,9 +27,9 @@ module "az_ad_application" { module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password lacework_integration_name = "Test Activity Log Rename" - service_principal_id = "module.az_ad_application.service_principal_id" + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/renamed_config.tf b/lwgenerate/azure/test-data/renamed_config.tf index 8c96b5606..15b9f5c84 100644 --- a/lwgenerate/azure/test-data/renamed_config.tf +++ b/lwgenerate/azure/test-data/renamed_config.tf @@ -27,9 +27,9 @@ module "az_ad_application" { module "az_config" { source = "lacework/config/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password lacework_integration_name = "Test Config Rename" - service_principal_id = "module.az_ad_application.service_principal_id" + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } diff --git a/lwgenerate/azure/test-data/renamed_config_and_activity_log.tf b/lwgenerate/azure/test-data/renamed_config_and_activity_log.tf index 2617b7ed5..799039d91 100644 --- a/lwgenerate/azure/test-data/renamed_config_and_activity_log.tf +++ b/lwgenerate/azure/test-data/renamed_config_and_activity_log.tf @@ -27,19 +27,19 @@ module "az_ad_application" { module "az_config" { source = "lacework/config/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password lacework_integration_name = "Test Config Rename" - service_principal_id = "module.az_ad_application.service_principal_id" + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true } module "az_activity_log" { source = "lacework/activity-log/azure" version = "~> 1.0" - application_id = "module.az_ad_application.application_id" - application_password = "module.az_ad_application.application_password" + application_id = module.az_ad_application.application_id + application_password = module.az_ad_application.application_password lacework_integration_name = "Test Activity Log Rename" - service_principal_id = "module.az_ad_application.service_principal_id" + service_principal_id = module.az_ad_application.service_principal_id use_existing_ad_application = true }