Skip to content

Commit

Permalink
Add Cloud SCC Source resource (#1033)
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored and rileykarson committed Aug 14, 2019
1 parent 804c51e commit 5a6917d
Show file tree
Hide file tree
Showing 9 changed files with 442 additions and 9 deletions.
1 change: 1 addition & 0 deletions google-beta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type Config struct {
PubsubBasePath string
RedisBasePath string
ResourceManagerBasePath string
SecurityCenterBasePath string
SecurityScannerBasePath string
SourceRepoBasePath string
SpannerBasePath string
Expand Down
4 changes: 4 additions & 0 deletions google-beta/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func Provider() terraform.ResourceProvider {
PubsubCustomEndpointEntryKey: PubsubCustomEndpointEntry,
RedisCustomEndpointEntryKey: RedisCustomEndpointEntry,
ResourceManagerCustomEndpointEntryKey: ResourceManagerCustomEndpointEntry,
SecurityCenterCustomEndpointEntryKey: SecurityCenterCustomEndpointEntry,
SecurityScannerCustomEndpointEntryKey: SecurityScannerCustomEndpointEntry,
SourceRepoCustomEndpointEntryKey: SourceRepoCustomEndpointEntry,
SpannerCustomEndpointEntryKey: SpannerCustomEndpointEntry,
Expand Down Expand Up @@ -244,6 +245,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
GeneratedPubsubResourcesMap,
GeneratedRedisResourcesMap,
GeneratedResourceManagerResourcesMap,
GeneratedSecurityCenterResourcesMap,
GeneratedSecurityScannerResourcesMap,
GeneratedSourceRepoResourcesMap,
GeneratedSpannerResourcesMap,
Expand Down Expand Up @@ -437,6 +439,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
config.PubsubBasePath = d.Get(PubsubCustomEndpointEntryKey).(string)
config.RedisBasePath = d.Get(RedisCustomEndpointEntryKey).(string)
config.ResourceManagerBasePath = d.Get(ResourceManagerCustomEndpointEntryKey).(string)
config.SecurityCenterBasePath = d.Get(SecurityCenterCustomEndpointEntryKey).(string)
config.SecurityScannerBasePath = d.Get(SecurityScannerCustomEndpointEntryKey).(string)
config.SourceRepoBasePath = d.Get(SourceRepoCustomEndpointEntryKey).(string)
config.SpannerBasePath = d.Get(SpannerCustomEndpointEntryKey).(string)
Expand Down Expand Up @@ -501,6 +504,7 @@ func ConfigureBasePaths(c *Config) {
c.PubsubBasePath = PubsubDefaultBasePath
c.RedisBasePath = RedisDefaultBasePath
c.ResourceManagerBasePath = ResourceManagerDefaultBasePath
c.SecurityCenterBasePath = SecurityCenterDefaultBasePath
c.SecurityScannerBasePath = SecurityScannerDefaultBasePath
c.SourceRepoBasePath = SourceRepoDefaultBasePath
c.SpannerBasePath = SpannerDefaultBasePath
Expand Down
34 changes: 34 additions & 0 deletions google-beta/provider_security_center_gen.go
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(),
}
12 changes: 3 additions & 9 deletions google-beta/resource_kms_key_ring.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,9 @@ func resourceKmsKeyRingRead(d *schema.ResourceData, meta interface{}) error {
}

func resourceKmsKeyRingDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

keyRingId, err := parseKmsKeyRingId(d.Id(), config)
if err != nil {
return err
}

log.Printf("[WARNING] KMS KeyRing resources cannot be deleted from GCP. This KeyRing %s will be removed from Terraform state, but will still be present on the server.", keyRingId.keyRingId())

log.Printf("[WARNING] Kms KeyRing 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
Expand Down
238 changes: 238 additions & 0 deletions google-beta/resource_security_center_source.go
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
}
49 changes: 49 additions & 0 deletions google-beta/resource_security_center_source_test.go
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)
}
1 change: 1 addition & 0 deletions website/docs/provider_reference.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ be used for configuration are below:
* `resource_manager_custom_endpoint` (`GOOGLE_RESOURCE_MANAGER_CUSTOM_ENDPOINT`) - `https://cloudresourcemanager.googleapis.com/v1/`
* `resource_manager_v2beta1_custom_endpoint` (`GOOGLE_RESOURCE_MANAGER_V2BETA1_CUSTOM_ENDPOINT`) - `https://cloudresourcemanager.googleapis.com/v2beta1/`
* `runtimeconfig_custom_endpoint` (`GOOGLE_RUNTIMECONFIG_CUSTOM_ENDPOINT`) - `https://runtimeconfig.googleapis.com/v1beta1/`
* `security_center_custom_endpoints` (`GOOGLE_SECURITY_CENTER_CUSTOM_ENDPOINT`) - `https://securitycenter.googleapis.com/v1/`
* `service_management_custom_endpoint` (`GOOGLE_SERVICE_MANAGEMENT_CUSTOM_ENDPOINT`) - `https://servicemanagement.googleapis.com/v1/`
* `service_networking_custom_endpoint` (`GOOGLE_SERVICE_NETWORKING_CUSTOM_ENDPOINT`) - `https://servicenetworking.googleapis.com/v1/`
* `service_usage_custom_endpoint` (`GOOGLE_SERVICE_USAGE_CUSTOM_ENDPOINT`) - `https://serviceusage.googleapis.com/v1/`
Expand Down
Loading

0 comments on commit 5a6917d

Please sign in to comment.