diff --git a/examples/resource_lacework_integration_gcp_org_agentless_scanning/main.tf b/examples/resource_lacework_integration_gcp_org_agentless_scanning/main.tf index 20a042bb..361790a9 100644 --- a/examples/resource_lacework_integration_gcp_org_agentless_scanning/main.tf +++ b/examples/resource_lacework_integration_gcp_org_agentless_scanning/main.tf @@ -7,12 +7,12 @@ terraform { } provider "lacework" { - organization = true + organization = true } -variable "name" { +variable "integration_name" { type = string - default = "GCP Agentless Scanning org_example" + default = "GCP Agentless Scanning Example" } variable "client_id" { @@ -47,7 +47,7 @@ variable "integration_type" { variable "project_id" { type = string - default = "org-example-project-id" + default = "example-project-id" } variable "bucket_name" { @@ -55,7 +55,7 @@ variable "bucket_name" { default = "storage bucket id" } -variable "scanning_project_id" { +variable "scanning-project-id" { type = string default = "scanning-project-id" } @@ -70,11 +70,6 @@ variable "filter_list" { default = ["proj1", "proj2"] } -variable "scan_frequency" { - type = number - default = 24 -} - variable "org_account_mappings" { type = list(object({ default_lacework_account = string @@ -88,7 +83,7 @@ variable "org_account_mappings" { } resource "lacework_integration_gcp_agentless_scanning" "org_example" { - name = var.name + name = var.integration_name credentials { client_id = var.client_id client_email = var.client_email @@ -97,14 +92,14 @@ resource "lacework_integration_gcp_agentless_scanning" "org_example" { token_uri = var.token_uri } resource_level = "ORGANIZATION" - resource_id = "techally-test" - bucket_name = var.bucket_name - scanning_project_id = "gcp-lw-scanner" - scan_frequency = var.scan_frequency + resource_id = "294451184225" + scanning_project_id = "techally-test" + scan_frequency = 24 scan_containers = true scan_host_vulnerabilities = true scan_multi_volume = false scan_stopped_instances = true + bucket_name = var.bucket_name query_text = var.query_text filter_list = var.filter_list @@ -154,4 +149,4 @@ output "server_token" { output "org_account_mappings" { value = lacework_integration_gcp_agentless_scanning.org_example.org_account_mappings -} \ No newline at end of file +} diff --git a/integration/integration.go b/integration/integration.go index d353f4fe..fb58f76e 100644 --- a/integration/integration.go +++ b/integration/integration.go @@ -189,7 +189,6 @@ func GetContainerRegisteryGar(result string) api.GcpGarIntegrationResponse { func GetGcpAgentlessScanningResponse(result string) api.GcpSidekickIntegrationResponse { id := GetIDFromTerraResults(result) - res, err := LwClient.V2.CloudAccounts.GetGcpSidekick(id) if err != nil { @@ -199,6 +198,17 @@ func GetGcpAgentlessScanningResponse(result string) api.GcpSidekickIntegrationRe return res } +func GetGcpAgentlessOrgScanningResponse(result string) api.GcpSidekickIntegrationResponse { + id := GetIDFromTerraResults(result) + res, err := LwOrgClient.V2.CloudAccounts.GetGcpSidekick(id) + + if err != nil { + log.Fatalf("Unable to find integration id: %s\n Response: %v", id, res) + } + + return res +} + func GetContainerRegisteryGcr(result string) api.GcpGcrIntegrationResponse { id := GetIDFromTerraResults(result) diff --git a/integration/resource_lacework_integration_gcp_agentless_scanning_test.go b/integration/resource_lacework_integration_gcp_agentless_scanning_test.go index 4300229a..8586f214 100644 --- a/integration/resource_lacework_integration_gcp_agentless_scanning_test.go +++ b/integration/resource_lacework_integration_gcp_agentless_scanning_test.go @@ -47,66 +47,3 @@ func TestIntegrationGcpAgentlessScanningCreate(t *testing.T) { assert.Equal(t, update_integration_name, updateData.Data.Name) } } - -func TestIntegrationGcpAgentlessOrgScanningCreate(t *testing.T) { - gcreds, err := googleLoadDefaultCredentials() - integration_name := "GCP Agentless Scanning Example Integration Test" - update_integration_name := fmt.Sprintf("%s Updated", integration_name) - if assert.Nil(t, err, "this test requires you to set GOOGLE_CREDENTIALS environment variable") { - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: "../examples/resource_lacework_integration_gcp_org_agentless_scanning", - Vars: map[string]interface{}{ - "name": integration_name, - "client_id": gcreds.ClientID, - "client_email": gcreds.ClientEmail, - "private_key_id": gcreds.PrivateKeyID, - "bucket_name": "storage bucket id", - "org_account_mappings": []map[string]interface{}{ - { - "default_lacework_account": "customerdemo", - "mapping": []map[string]interface{}{ - { - "lacework_account": "abc", - "gcp_projects": []string{"lw-scanner-5"}, - }, - }, - }, - }, - }, - EnvVars: map[string]string{ - "TF_VAR_private_key": gcreds.PrivateKey, - "LW_API_TOKEN": LwApiToken, - }, - }) - defer terraform.Destroy(t, terraformOptions) - - // Create new Google Agentless Scanning integration - create := terraform.InitAndApplyAndIdempotent(t, terraformOptions) - createData := GetGcpAgentlessScanningResponse(create) - assert.Equal(t, integration_name, createData.Data.Name) - - // Update Gcp integration - terraformOptions.Vars = map[string]interface{}{ - "name": update_integration_name, - "client_id": gcreds.ClientID, - "client_email": gcreds.ClientEmail, - "private_key_id": gcreds.PrivateKeyID, - "bucket_name": "storage bucket id", - "org_account_mappings": []map[string]interface{}{ - { - "default_lacework_account": "customerdemo", - "mapping": []map[string]interface{}{ - { - "lacework_account": "abc", - "gcp_projects": []string{"lw-scanner-5"}, - }, - }, - }, - }, - } - - update := terraform.ApplyAndIdempotent(t, terraformOptions) - updateData := GetGcpAgentlessScanningResponse(update) - assert.Equal(t, update_integration_name, updateData.Data.Name) - } -} diff --git a/integration/resource_lacework_integration_gcp_org_agentless_scanning_test.go b/integration/resource_lacework_integration_gcp_org_agentless_scanning_test.go new file mode 100644 index 00000000..26035fa2 --- /dev/null +++ b/integration/resource_lacework_integration_gcp_org_agentless_scanning_test.go @@ -0,0 +1,72 @@ +package integration + +import ( + "fmt" + "testing" + + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" +) + +func TestIntegrationGcpAgentlessOrgScanningCreateAndUpdate(t *testing.T) { + gcreds, err := googleLoadDefaultCredentials() + integration_name := "GCP Org Agentless Scanning Example Integration Test" + update_integration_name := fmt.Sprintf("%s Updated", integration_name) + if assert.Nil(t, err, "this test requires you to set GOOGLE_CREDENTIALS environment variable") { + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: "../examples/resource_lacework_integration_gcp_org_agentless_scanning", + Vars: map[string]interface{}{ + "integration_name": integration_name, + "client_id": gcreds.ClientID, + "client_email": gcreds.ClientEmail, + "private_key_id": gcreds.PrivateKeyID, + "bucket_name": "storage bucket id", + "org_account_mappings": []map[string]interface{}{ + { + "default_lacework_account": "customerdemo", + "mapping": []map[string]interface{}{ + { + "lacework_account": "tech-ally", + "gcp_projects": []string{"techally-test"}, + }, + }, + }, + }, + }, + EnvVars: map[string]string{ + "TF_VAR_private_key": gcreds.PrivateKey, + "LW_API_TOKEN": LwApiToken, + }, + }) + defer terraform.Destroy(t, terraformOptions) + + // Create new Google Agentless Scanning integration + create := terraform.InitAndApplyAndIdempotent(t, terraformOptions) + createData := GetGcpAgentlessOrgScanningResponse(create) + assert.Equal(t, integration_name, createData.Data.Name) + + // Update Gcp integration + terraformOptions.Vars = map[string]interface{}{ + "integration_name": update_integration_name, + "client_id": gcreds.ClientID, + "client_email": gcreds.ClientEmail, + "private_key_id": gcreds.PrivateKeyID, + "bucket_name": "storage bucket id", + "org_account_mappings": []map[string]interface{}{ + { + "default_lacework_account": "customerdemo", + "mapping": []map[string]interface{}{ + { + "lacework_account": "abc", + "gcp_projects": []string{"techally-test"}, + }, + }, + }, + }, + } + + update := terraform.ApplyAndIdempotent(t, terraformOptions) + updateData := GetGcpAgentlessOrgScanningResponse(update) + assert.Equal(t, update_integration_name, updateData.Data.Name) + } +} diff --git a/lacework/account_mapping_helper.go b/lacework/account_mapping_helper.go index e717575b..d3f3d158 100644 --- a/lacework/account_mapping_helper.go +++ b/lacework/account_mapping_helper.go @@ -5,7 +5,7 @@ import ( ) type accountMappingsFile struct { - DefaultLaceworkAccount string `json:"defaultLaceworkAccountAws"` + DefaultLaceworkAccount string `json:"defaultLaceworkAccount"` Mappings map[string]interface{} `json:"integration_mappings"` } @@ -30,13 +30,18 @@ func getResourceOrgAccountMappings(d *schema.ResourceData, mappingsType string) mappingSet := accountMappings["mapping"].(*schema.Set) for _, m := range mappingSet.List() { mapping := m.(map[string]interface{}) - accountMapFile.Mappings[mapping["lacework_account"].(string)] = map[string]interface{}{ - mappingsType: castStringSlice(mapping[mappingsType].(*schema.Set).List()), + if mappingsType == "gcp_projects" { + accountMapFile.Mappings[mapping["lacework_account"].(string)] = map[string]interface{}{ + "gcp_projects": castStringSlice(mapping[mappingsType].(*schema.Set).List()), + } + } else { + accountMapFile.Mappings[mapping["lacework_account"].(string)] = map[string]interface{}{ + "aws_accounts": castStringSlice(mapping[mappingsType].(*schema.Set).List()), + } } } } - return accountMapFile } @@ -58,21 +63,57 @@ func flattenOrgAccountMappings(mappingFile *accountMappingsFile, mappingsType st func flattenMappings(mappings map[string]interface{}, mappingsType string) *schema.Set { var ( - orgAccountMappingsSchema = awsCloudTrailIntegrationSchema["org_account_mappings"].Elem.(*schema.Resource) - mappingSchema = orgAccountMappingsSchema.Schema["mapping"].Elem.(*schema.Resource) - accountsSchema = mappingSchema.Schema[mappingsType].Elem.(*schema.Schema) - res = schema.NewSet(schema.HashResource(mappingSchema), []interface{}{}) + awsOrgAccountMappingsSchema = awsCloudTrailIntegrationSchema["org_account_mappings"].Elem.(*schema.Resource) + awsMappingSchema = awsOrgAccountMappingsSchema.Schema["mapping"].Elem.(*schema.Resource) + awsAccountsSchema = awsMappingSchema.Schema[mappingsType].Elem.(*schema.Schema) + awsRes = schema.NewSet(schema.HashResource(awsMappingSchema), []interface{}{}) ) for laceworkAccount, m := range mappings { mappingValue := m.(map[string]interface{}) - res.Add(map[string]interface{}{ + awsRes.Add(map[string]interface{}{ "lacework_account": laceworkAccount, - mappingsType: schema.NewSet(schema.HashSchema(accountsSchema), - mappingValue[mappingsType].([]interface{}), + "aws_accounts": schema.NewSet(schema.HashSchema(awsAccountsSchema), + mappingValue["aws_accounts"].([]interface{}), ), }) } - return res + return awsRes +} + +func flattenOrgGcpAccountMappings(mappingFile *accountMappingsFile) []map[string]interface{} { + orgAccMappings := make([]map[string]interface{}, 0, 1) + + if mappingFile.Empty() { + return orgAccMappings + } + + mappings := map[string]interface{}{ + "default_lacework_account": mappingFile.DefaultLaceworkAccount, + "mapping": flattenGcpMappings(mappingFile.Mappings), + } + + orgAccMappings = append(orgAccMappings, mappings) + return orgAccMappings +} + +func flattenGcpMappings(mappings map[string]interface{}) *schema.Set { + var ( + gcpOrgAccountMappingsSchema = gcpAgentlessScanningIntegrationSchema["org_account_mappings"].Elem.(*schema.Resource) + gcpMappingSchema = gcpOrgAccountMappingsSchema.Schema["mapping"].Elem.(*schema.Resource) + gcpAccountsSchema = gcpMappingSchema.Schema["mapping"].Elem.(*schema.Schema) + gcpRes = schema.NewSet(schema.HashResource(gcpMappingSchema), []interface{}{}) + ) + + for laceworkAccount, m := range mappings { + mappingValue := m.(map[string]interface{}) + gcpRes.Add(map[string]interface{}{ + "lacework_account": laceworkAccount, + "gcp_projects": schema.NewSet(schema.HashSchema(gcpAccountsSchema), + mappingValue["gcp_projects"].([]interface{}), + ), + }) + } + return gcpRes } diff --git a/lacework/resource_lacework_integration_gcp_agentless_scanning.go b/lacework/resource_lacework_integration_gcp_agentless_scanning.go index 6e334a14..1ccbfb0a 100644 --- a/lacework/resource_lacework_integration_gcp_agentless_scanning.go +++ b/lacework/resource_lacework_integration_gcp_agentless_scanning.go @@ -19,233 +19,233 @@ func resourceLaceworkIntegrationGcpAgentlessScanning() *schema.Resource { Read: resourceLaceworkIntegrationGcpAgentlessScanningRead, Update: resourceLaceworkIntegrationGcpAgentlessScanningUpdate, Delete: resourceLaceworkIntegrationGcpAgentlessScanningDelete, - + Schema: gcpAgentlessScanningIntegrationSchema, Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + } +} - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "The integration name.", - }, - "intg_guid": { - Type: schema.TypeString, - Computed: true, - }, - "enabled": { - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "The state of the external integration.", - }, - "retries": { - Type: schema.TypeInt, - Optional: true, - Default: 5, - Description: "The number of attempts to create the external integration.", - }, - "credentials": { - Type: schema.TypeList, - MaxItems: 1, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "client_id": { - Type: schema.TypeString, - Required: true, - Description: "Client Id from credentials file.", - }, - "private_key_id": { - Type: schema.TypeString, - Required: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - return !d.HasChanges( - "name", "resource_level", "resource_id", "org_level", "enabled", - "credentials.0.client_id", - "credentials.0.client_email", - ) - }, - Description: "Private Key Id from credentials file.", - }, - "client_email": { - Type: schema.TypeString, - Required: true, - Description: "Client email from credentials file.", - }, - "private_key": { - Type: schema.TypeString, - Required: true, - Sensitive: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - // @afiune we can't compare this element since our API, for security reasons, - // does NOT return the private key configured in the Lacework server. So if - // any other element changed from the credentials then we trigger a diff - return !d.HasChanges( - "name", "resource_level", "resource_id", "org_level", "enabled", - "credentials.0.client_id", - "credentials.0.client_email", - ) - }, - Description: "Private Key from credentials file.", - }, - "token_uri": { - Type: schema.TypeString, - Optional: true, - Default: "https://oauth2.googleapis.com/token", - Description: "Token URI from credentials file.", - }, +var gcpAgentlessScanningIntegrationSchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The integration name.", + }, + "intg_guid": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "The state of the external integration.", + }, + "retries": { + Type: schema.TypeInt, + Optional: true, + Default: 5, + Description: "The number of attempts to create the external integration.", + }, + "credentials": { + Type: schema.TypeList, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_id": { + Type: schema.TypeString, + Required: true, + Description: "Client Id from credentials file.", + }, + "private_key_id": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return !d.HasChanges( + "name", "resource_level", "resource_id", "org_level", "enabled", + "credentials.0.client_id", + "credentials.0.client_email", + ) }, + Description: "Private Key Id from credentials file.", }, - }, - "resource_level": { - Type: schema.TypeString, - Optional: true, - Default: api.GcpProjectIntegration.String(), - StateFunc: func(val interface{}) string { - return strings.ToUpper(val.(string)) + "client_email": { + Type: schema.TypeString, + Required: true, + Description: "Client email from credentials file.", }, - ValidateFunc: func(value interface{}, key string) ([]string, []error) { - switch strings.ToUpper(value.(string)) { - case api.GcpProjectIntegration.String(), - api.GcpOrganizationIntegration.String(): - return nil, nil - default: - return nil, []error{ - fmt.Errorf("%s: can only be either '%s' or '%s'", - key, - api.GcpProjectIntegration.String(), - api.GcpOrganizationIntegration.String()), - } - } + "private_key": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // @afiune we can't compare this element since our API, for security reasons, + // does NOT return the private key configured in the Lacework server. So if + // any other element changed from the credentials then we trigger a diff + return !d.HasChanges( + "name", "resource_level", "resource_id", "org_level", "enabled", + "credentials.0.client_id", + "credentials.0.client_email", + ) + }, + Description: "Private Key from credentials file.", + }, + "token_uri": { + Type: schema.TypeString, + Optional: true, + Default: "https://oauth2.googleapis.com/token", + Description: "Token URI from credentials file.", }, - Description: "Integration level - ORGANIZATION / PROJECT.", - }, - "resource_id": { - Type: schema.TypeString, - Required: true, - Description: "Organization Id or Project Id.", - }, - "created_or_updated_time": { - Type: schema.TypeString, - Computed: true, - }, - "created_or_updated_by": { - Type: schema.TypeString, - Computed: true, - }, - "type_name": { - Type: schema.TypeString, - Computed: true, - }, - "org_level": { - Type: schema.TypeBool, - Computed: true, - }, - "server_token": { - Type: schema.TypeString, - Computed: true, - }, - "uri": { - Type: schema.TypeString, - Computed: true, - }, - "bucket_name": { - Type: schema.TypeString, - Required: true, - Description: "Bucket containing analysis results shared with Lacework platform.", - }, - "scanning_project_id": { - Type: schema.TypeString, - Required: true, - Description: "Project ID where scanner is deployed.", - }, - "scan_frequency": { - Type: schema.TypeInt, - Optional: true, - Default: 24, - Description: "How often in hours the scan will run in hours.", - }, - "scan_containers": { - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "Whether to includes scanning for containers.", - }, - "scan_host_vulnerabilities": { - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "Whether to includes scanning for host vulnerabilities.", - }, - "scan_multi_volume": { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Whether to scan secondary volumes (true) or only root volumes (false)", - }, - "scan_stopped_instances": { - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "Whether to scan stopped instances (true)", }, - "query_text": { - Type: schema.TypeString, - Optional: true, - Default: "", - Description: "The LQL query text.", + }, + }, + "resource_level": { + Type: schema.TypeString, + Optional: true, + Default: api.GcpProjectIntegration.String(), + StateFunc: func(val interface{}) string { + return strings.ToUpper(val.(string)) + }, + ValidateFunc: func(value interface{}, key string) ([]string, []error) { + switch strings.ToUpper(value.(string)) { + case api.GcpProjectIntegration.String(), + api.GcpOrganizationIntegration.String(): + return nil, nil + default: + return nil, []error{ + fmt.Errorf("%s: can only be either '%s' or '%s'", + key, + api.GcpProjectIntegration.String(), + api.GcpOrganizationIntegration.String()), + } + } + }, + Description: "Integration level - ORGANIZATION / PROJECT.", + }, + "resource_id": { + Type: schema.TypeString, + Required: true, + Description: "Organization Id or Project Id.", + }, + "created_or_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_or_updated_by": { + Type: schema.TypeString, + Computed: true, + }, + "type_name": { + Type: schema.TypeString, + Computed: true, + }, + "org_level": { + Type: schema.TypeBool, + Computed: true, + }, + "server_token": { + Type: schema.TypeString, + Computed: true, + }, + "uri": { + Type: schema.TypeString, + Computed: true, + }, + "bucket_name": { + Type: schema.TypeString, + Required: true, + Description: "Bucket containing analysis results shared with Lacework platform.", + }, + "scanning_project_id": { + Type: schema.TypeString, + Required: true, + Description: "Project ID where scanner is deployed.", + }, + "scan_frequency": { + Type: schema.TypeInt, + Optional: true, + Default: 24, + Description: "How often in hours the scan will run in hours.", + }, + "scan_containers": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Whether to includes scanning for containers.", + }, + "scan_host_vulnerabilities": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Whether to includes scanning for host vulnerabilities.", + }, + "scan_multi_volume": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to scan secondary volumes (true) or only root volumes (false)", + }, + "scan_stopped_instances": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Whether to scan stopped instances (true)", + }, + "query_text": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "The LQL query text.", + }, + "filter_list": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + StateFunc: func(val interface{}) string { + return strings.TrimSpace(val.(string)) }, - "filter_list": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - StateFunc: func(val interface{}) string { - return strings.TrimSpace(val.(string)) - }, + }, + Default: nil, + Description: "List of Projects to specifically include/exclude.", + }, + "org_account_mappings": { + Type: schema.TypeList, + Optional: true, + Description: "Mapping of GCP projects to Lacework accounts within a Lacework organization.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_lacework_account": { + Type: schema.TypeString, + Required: true, + Description: "The default Lacework account name where any non-mapped GCP project will appear", }, - Default: nil, - Description: "List of Projects to specifically include/exclude.", - }, - "org_account_mappings": { - Type: schema.TypeList, - Optional: true, - Description: "Mapping of GCP projects to Lacework accounts within a Lacework organization.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "default_lacework_account": { - Type: schema.TypeString, - Required: true, - Description: "The default Lacework account name where any non-mapped GCP project will appear", - }, - "mapping": { - Type: schema.TypeSet, - Required: true, - Description: "A map of GCP projects to Lacework account. This can be specified multiple times to map multiple Lacework accounts.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "lacework_account": { - Type: schema.TypeString, - Required: true, - Description: "The Lacework account name where the Agentless activity from the selected gcp projects will appear.", - }, - "gcp_projects": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - MinItems: 1, - Required: true, - Description: "The list of GCP project IDs to map.", - }, - }, + "mapping": { + Type: schema.TypeSet, + Required: true, + Description: "A map of GCP projects to Lacework account. This can be specified multiple times to map multiple Lacework accounts.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lacework_account": { + Type: schema.TypeString, + Required: true, + Description: "The Lacework account name where the Agentless activity from the selected gcp projects will appear.", + }, + "gcp_projects": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + MinItems: 1, + Required: true, + Description: "The list of GCP project IDs to map.", }, }, }, }, }, }, - } + }, } func resourceLaceworkIntegrationGcpAgentlessScanningCreate(d *schema.ResourceData, meta interface{}) error { @@ -255,11 +255,10 @@ func resourceLaceworkIntegrationGcpAgentlessScanningCreate(d *schema.ResourceDat resourceLevel = api.GcpProjectIntegration ) - if strings.ToUpper( - d.Get("resource_level").(string), - ) == api.GcpOrganizationIntegration.String() { + if strings.ToUpper(d.Get("resource_level").(string)) == api.GcpOrganizationIntegration.String() { resourceLevel = api.GcpOrganizationIntegration } + log.Printf("[INFO] Creating %s integration\n", api.GcpSidekickCloudAccount.String()) gcpSidekickData := api.GcpSidekickData{ @@ -284,16 +283,14 @@ func resourceLaceworkIntegrationGcpAgentlessScanningCreate(d *schema.ResourceDat } // verify if the user provided an account mapping - if d.Get("resource_level") == api.GcpOrganizationIntegration { - accountMapFile := getResourceOrgAccountMappings(d, gcpMappingType) - if !accountMapFile.Empty() { - accountMapFileBytes, err := json.Marshal(accountMapFile) - if err != nil { - return err - } - - gcpSidekickData.EncodeAccountMappingFile(accountMapFileBytes) + accountMapFile := getResourceOrgAccountMappings(d, gcpMappingType) + if !accountMapFile.Empty() { + accountMapFileBytes, err := json.Marshal(accountMapFile) + if err != nil { + return err } + + gcpSidekickData.EncodeAccountMappingFile(accountMapFileBytes) } data := api.NewCloudAccount(d.Get("name").(string), @@ -407,7 +404,7 @@ func resourceLaceworkIntegrationGcpAgentlessScanningRead(d *schema.ResourceData, } - err = d.Set("org_account_mappings", flattenOrgAccountMappings(accountMapFile, gcpMappingType)) + err = d.Set("org_account_mappings", flattenOrgGcpAccountMappings(accountMapFile)) if err != nil { return fmt.Errorf("Error flattening organization account mapping: %s", err) }