-
Notifications
You must be signed in to change notification settings - Fork 263
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Cloud SCC Source resource (#1033)
Signed-off-by: Modular Magician <[email protected]>
- Loading branch information
1 parent
804c51e
commit 5a6917d
Showing
9 changed files
with
442 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// ---------------------------------------------------------------------------- | ||
// | ||
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** | ||
// | ||
// ---------------------------------------------------------------------------- | ||
// | ||
// This file is automatically generated by Magic Modules and manual | ||
// changes will be clobbered when the file is regenerated. | ||
// | ||
// Please read more about how to change this file in | ||
// .github/CONTRIBUTING.md. | ||
// | ||
// ---------------------------------------------------------------------------- | ||
|
||
package google | ||
|
||
import "github.com/hashicorp/terraform/helper/schema" | ||
|
||
// If the base path has changed as a result of your PR, make sure to update | ||
// the provider_reference page! | ||
var SecurityCenterDefaultBasePath = "https://securitycenter.googleapis.com/v1/" | ||
var SecurityCenterCustomEndpointEntryKey = "security_center_custom_endpoint" | ||
var SecurityCenterCustomEndpointEntry = &schema.Schema{ | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validateCustomEndpoint, | ||
DefaultFunc: schema.MultiEnvDefaultFunc([]string{ | ||
"GOOGLE_SECURITY_CENTER_CUSTOM_ENDPOINT", | ||
}, SecurityCenterDefaultBasePath), | ||
} | ||
|
||
var GeneratedSecurityCenterResourcesMap = map[string]*schema.Resource{ | ||
"google_scc_source": resourceSecurityCenterSource(), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
// ---------------------------------------------------------------------------- | ||
// | ||
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** | ||
// | ||
// ---------------------------------------------------------------------------- | ||
// | ||
// This file is automatically generated by Magic Modules and manual | ||
// changes will be clobbered when the file is regenerated. | ||
// | ||
// Please read more about how to change this file in | ||
// .github/CONTRIBUTING.md. | ||
// | ||
// ---------------------------------------------------------------------------- | ||
|
||
package google | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"reflect" | ||
"strings" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/helper/validation" | ||
) | ||
|
||
func resourceSecurityCenterSource() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceSecurityCenterSourceCreate, | ||
Read: resourceSecurityCenterSourceRead, | ||
Update: resourceSecurityCenterSourceUpdate, | ||
Delete: resourceSecurityCenterSourceDelete, | ||
|
||
Importer: &schema.ResourceImporter{ | ||
State: resourceSecurityCenterSourceImport, | ||
}, | ||
|
||
Timeouts: &schema.ResourceTimeout{ | ||
Create: schema.DefaultTimeout(4 * time.Minute), | ||
Update: schema.DefaultTimeout(4 * time.Minute), | ||
Delete: schema.DefaultTimeout(4 * time.Minute), | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"display_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validateRegexp(`[\p{L}\p{N}]({\p{L}\p{N}_- ]{0,30}[\p{L}\p{N}])?`), | ||
}, | ||
"organization": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"description": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validation.StringLenBetween(0, 1024), | ||
}, | ||
"name": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceSecurityCenterSourceCreate(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
obj := make(map[string]interface{}) | ||
descriptionProp, err := expandSecurityCenterSourceDescription(d.Get("description"), d, config) | ||
if err != nil { | ||
return err | ||
} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { | ||
obj["description"] = descriptionProp | ||
} | ||
displayNameProp, err := expandSecurityCenterSourceDisplayName(d.Get("display_name"), d, config) | ||
if err != nil { | ||
return err | ||
} else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { | ||
obj["displayName"] = displayNameProp | ||
} | ||
|
||
url, err := replaceVars(d, config, "{{SecurityCenterBasePath}}organizations/{{organization}}/sources") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("[DEBUG] Creating new Source: %#v", obj) | ||
res, err := sendRequestWithTimeout(config, "POST", "", url, obj, d.Timeout(schema.TimeoutCreate)) | ||
if err != nil { | ||
return fmt.Errorf("Error creating Source: %s", err) | ||
} | ||
|
||
// Store the ID now | ||
id, err := replaceVars(d, config, "{{name}}") | ||
if err != nil { | ||
return fmt.Errorf("Error constructing id: %s", err) | ||
} | ||
d.SetId(id) | ||
|
||
log.Printf("[DEBUG] Finished creating Source %q: %#v", d.Id(), res) | ||
|
||
// `name` is autogenerated from the api so needs to be set post-create | ||
name, ok := res["name"] | ||
if !ok { | ||
return fmt.Errorf("Create response didn't contain critical fields. Create may not have succeeded.") | ||
} | ||
d.Set("name", name.(string)) | ||
d.SetId(name.(string)) | ||
|
||
return resourceSecurityCenterSourceRead(d, meta) | ||
} | ||
|
||
func resourceSecurityCenterSourceRead(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
url, err := replaceVars(d, config, "{{SecurityCenterBasePath}}{{name}}") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
res, err := sendRequest(config, "GET", "", url, nil) | ||
if err != nil { | ||
return handleNotFoundError(err, d, fmt.Sprintf("SecurityCenterSource %q", d.Id())) | ||
} | ||
|
||
if err := d.Set("name", flattenSecurityCenterSourceName(res["name"], d)); err != nil { | ||
return fmt.Errorf("Error reading Source: %s", err) | ||
} | ||
if err := d.Set("description", flattenSecurityCenterSourceDescription(res["description"], d)); err != nil { | ||
return fmt.Errorf("Error reading Source: %s", err) | ||
} | ||
if err := d.Set("display_name", flattenSecurityCenterSourceDisplayName(res["displayName"], d)); err != nil { | ||
return fmt.Errorf("Error reading Source: %s", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceSecurityCenterSourceUpdate(d *schema.ResourceData, meta interface{}) error { | ||
config := meta.(*Config) | ||
|
||
obj := make(map[string]interface{}) | ||
descriptionProp, err := expandSecurityCenterSourceDescription(d.Get("description"), d, config) | ||
if err != nil { | ||
return err | ||
} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { | ||
obj["description"] = descriptionProp | ||
} | ||
displayNameProp, err := expandSecurityCenterSourceDisplayName(d.Get("display_name"), d, config) | ||
if err != nil { | ||
return err | ||
} else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { | ||
obj["displayName"] = displayNameProp | ||
} | ||
|
||
url, err := replaceVars(d, config, "{{SecurityCenterBasePath}}{{name}}") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Printf("[DEBUG] Updating Source %q: %#v", d.Id(), obj) | ||
updateMask := []string{} | ||
|
||
if d.HasChange("description") { | ||
updateMask = append(updateMask, "description") | ||
} | ||
|
||
if d.HasChange("display_name") { | ||
updateMask = append(updateMask, "displayName") | ||
} | ||
// updateMask is a URL parameter but not present in the schema, so replaceVars | ||
// won't set it | ||
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = sendRequestWithTimeout(config, "PATCH", "", url, obj, d.Timeout(schema.TimeoutUpdate)) | ||
|
||
if err != nil { | ||
return fmt.Errorf("Error updating Source %q: %s", d.Id(), err) | ||
} | ||
|
||
return resourceSecurityCenterSourceRead(d, meta) | ||
} | ||
|
||
func resourceSecurityCenterSourceDelete(d *schema.ResourceData, meta interface{}) error { | ||
log.Printf("[WARNING] SecurityCenter Source resources"+ | ||
" cannot be deleted from GCP. The resource %s will be removed from Terraform"+ | ||
" state, but will still be present on the server.", d.Id()) | ||
d.SetId("") | ||
|
||
return nil | ||
} | ||
|
||
func resourceSecurityCenterSourceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
config := meta.(*Config) | ||
|
||
// current import_formats can't import fields with forward slashes in their value | ||
if err := parseImportId([]string{"(?P<name>.+)"}, d, config); err != nil { | ||
return nil, err | ||
} | ||
|
||
stringParts := strings.Split(d.Get("name").(string), "/") | ||
if len(stringParts) != 4 { | ||
return nil, fmt.Errorf( | ||
"Saw %s when the name is expected to have shape %s", | ||
d.Get("name"), | ||
"organizations/{{organization}}/sources/{{source}}", | ||
) | ||
} | ||
|
||
d.Set("organization", fmt.Sprintf("%s", stringParts[1])) | ||
return []*schema.ResourceData{d}, nil | ||
} | ||
|
||
func flattenSecurityCenterSourceName(v interface{}, d *schema.ResourceData) interface{} { | ||
return v | ||
} | ||
|
||
func flattenSecurityCenterSourceDescription(v interface{}, d *schema.ResourceData) interface{} { | ||
return v | ||
} | ||
|
||
func flattenSecurityCenterSourceDisplayName(v interface{}, d *schema.ResourceData) interface{} { | ||
return v | ||
} | ||
|
||
func expandSecurityCenterSourceDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { | ||
return v, nil | ||
} | ||
|
||
func expandSecurityCenterSourceDisplayName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { | ||
return v, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
) | ||
|
||
func TestAccSecurityCenterSource_basic(t *testing.T) { | ||
t.Parallel() | ||
|
||
orgId := getTestOrgFromEnv(t) | ||
suffix := acctest.RandString(10) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccSecurityCenterSource_sccSourceBasicExample(orgId, suffix, "My description"), | ||
}, | ||
{ | ||
ResourceName: "google_scc_source.custom_source", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
{ | ||
Config: testAccSecurityCenterSource_sccSourceBasicExample(orgId, suffix, ""), | ||
}, | ||
{ | ||
ResourceName: "google_scc_source.custom_source", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccSecurityCenterSource_sccSourceBasicExample(orgId, suffix, description string) string { | ||
return fmt.Sprintf(` | ||
resource "google_scc_source" "custom_source" { | ||
display_name = "TFSrc %s" | ||
organization = "%s" | ||
description = "%s" | ||
} | ||
`, suffix, orgId, description) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.