From 744b4294446c61cbfc8f60667f9ec52539ceb0f0 Mon Sep 17 00:00:00 2001 From: The Magician Date: Thu, 14 Dec 2023 11:52:43 -0800 Subject: [PATCH] Add resources for Cloud Logging default settings (#9409) (#6754) * Add folder and organization setting resources * Try GetTestOrgTargetFromEnv instead of GetTestOrgFromEnv. * Use correct organization for testing. * Remove key rotation to fix VCR test. * Don't specify fields matching default values. * Specify true instead of yes. * Use BootstrapKMSKeyInLocation instead of creating new keys. * Add missing quotes * Add additional examples to generate additional tests. * Remove unneeded resources from examples. * Simplify tests to be one full resource creation and one update. * Fix typo in test * Document and cleanup example. [upstream:5e89b3b459087978b89d3405bd4821d9af1436f3] Signed-off-by: Modular Magician --- .changelog/9409.txt | 6 + .../provider/provider_mmv1_resources.go | 6 +- .../resource_logging_folder_settings.go | 336 ++++++++++++++++++ ..._logging_folder_settings_generated_test.go | 80 +++++ .../resource_logging_folder_settings_test.go | 88 +++++ .../resource_logging_organization_settings.go | 336 ++++++++++++++++++ ...urce_logging_organization_settings_test.go | 76 ++++ .../r/logging_folder_settings.html.markdown | 133 +++++++ ...ogging_organization_settings.html.markdown | 128 +++++++ 9 files changed, 1187 insertions(+), 2 deletions(-) create mode 100644 .changelog/9409.txt create mode 100644 google-beta/services/logging/resource_logging_folder_settings.go create mode 100644 google-beta/services/logging/resource_logging_folder_settings_generated_test.go create mode 100644 google-beta/services/logging/resource_logging_folder_settings_test.go create mode 100644 google-beta/services/logging/resource_logging_organization_settings.go create mode 100644 google-beta/services/logging/resource_logging_organization_settings_test.go create mode 100644 website/docs/r/logging_folder_settings.html.markdown create mode 100644 website/docs/r/logging_organization_settings.html.markdown diff --git a/.changelog/9409.txt b/.changelog/9409.txt new file mode 100644 index 0000000000..85214fc27b --- /dev/null +++ b/.changelog/9409.txt @@ -0,0 +1,6 @@ +```release-note:new-resource +`google_logging_folder_settings` +``` +```release-note:new-resource +`google_logging_organization_settings` +``` diff --git a/google-beta/provider/provider_mmv1_resources.go b/google-beta/provider/provider_mmv1_resources.go index 7742f65f25..1afc83097e 100644 --- a/google-beta/provider/provider_mmv1_resources.go +++ b/google-beta/provider/provider_mmv1_resources.go @@ -417,9 +417,9 @@ var handwrittenIAMDatasources = map[string]*schema.Resource{ } // Resources -// Generated resources: 404 +// Generated resources: 406 // Generated IAM resources: 252 -// Total generated resources: 656 +// Total generated resources: 658 var generatedResources = map[string]*schema.Resource{ "google_folder_access_approval_settings": accessapproval.ResourceAccessApprovalFolderSettings(), "google_organization_access_approval_settings": accessapproval.ResourceAccessApprovalOrganizationSettings(), @@ -892,9 +892,11 @@ var generatedResources = map[string]*schema.Resource{ "google_kms_key_ring": kms.ResourceKMSKeyRing(), "google_kms_key_ring_import_job": kms.ResourceKMSKeyRingImportJob(), "google_kms_secret_ciphertext": kms.ResourceKMSSecretCiphertext(), + "google_logging_folder_settings": logging.ResourceLoggingFolderSettings(), "google_logging_linked_dataset": logging.ResourceLoggingLinkedDataset(), "google_logging_log_view": logging.ResourceLoggingLogView(), "google_logging_metric": logging.ResourceLoggingMetric(), + "google_logging_organization_settings": logging.ResourceLoggingOrganizationSettings(), "google_looker_instance": looker.ResourceLookerInstance(), "google_memcache_instance": memcache.ResourceMemcacheInstance(), "google_migration_center_group": migrationcenter.ResourceMigrationCenterGroup(), diff --git a/google-beta/services/logging/resource_logging_folder_settings.go b/google-beta/services/logging/resource_logging_folder_settings.go new file mode 100644 index 0000000000..0b6dc16066 --- /dev/null +++ b/google-beta/services/logging/resource_logging_folder_settings.go @@ -0,0 +1,336 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// 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 logging + +import ( + "fmt" + "log" + "reflect" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" +) + +func ResourceLoggingFolderSettings() *schema.Resource { + return &schema.Resource{ + Create: resourceLoggingFolderSettingsCreate, + Read: resourceLoggingFolderSettingsRead, + Update: resourceLoggingFolderSettingsUpdate, + Delete: resourceLoggingFolderSettingsDelete, + + Importer: &schema.ResourceImporter{ + State: resourceLoggingFolderSettingsImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "folder": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The folder for which to retrieve settings.`, + }, + "disable_default_sink": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `If set to true, the _Default sink in newly created projects and folders will created in a disabled state. This can be used to automatically disable log storage if there is already an aggregated sink configured in the hierarchy. The _Default sink can be re-enabled manually if needed.`, + }, + "kms_key_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `The resource name for the configured Cloud KMS key.`, + }, + "storage_location": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `The storage location that Cloud Logging will use to create new resources when a location is needed but not explicitly provided.`, + }, + "kms_service_account_id": { + Type: schema.TypeString, + Computed: true, + Description: `The service account that will be used by the Log Router to access your Cloud KMS key.`, + }, + "logging_service_account_id": { + Type: schema.TypeString, + Computed: true, + Description: `The service account for the given container. Sinks use this service account as their writerIdentity if no custom service account is provided.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The resource name of the settings.`, + }, + }, + UseJSONNumber: true, + } +} + +func resourceLoggingFolderSettingsCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + kmsKeyNameProp, err := expandLoggingFolderSettingsKmsKeyName(d.Get("kms_key_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("kms_key_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(kmsKeyNameProp)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) { + obj["kmsKeyName"] = kmsKeyNameProp + } + storageLocationProp, err := expandLoggingFolderSettingsStorageLocation(d.Get("storage_location"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("storage_location"); !tpgresource.IsEmptyValue(reflect.ValueOf(storageLocationProp)) && (ok || !reflect.DeepEqual(v, storageLocationProp)) { + obj["storageLocation"] = storageLocationProp + } + disableDefaultSinkProp, err := expandLoggingFolderSettingsDisableDefaultSink(d.Get("disable_default_sink"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("disable_default_sink"); !tpgresource.IsEmptyValue(reflect.ValueOf(disableDefaultSinkProp)) && (ok || !reflect.DeepEqual(v, disableDefaultSinkProp)) { + obj["disableDefaultSink"] = disableDefaultSinkProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LoggingBasePath}}folders/{{folder}}/settings?updateMask=disableDefaultSink,storageLocation,kmsKeyName") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new FolderSettings: %#v", obj) + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + }) + if err != nil { + return fmt.Errorf("Error creating FolderSettings: %s", err) + } + if err := d.Set("name", flattenLoggingFolderSettingsName(res["name"], d, config)); err != nil { + return fmt.Errorf(`Error setting computed identity field "name": %s`, err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "folders/{{folder}}/settings") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating FolderSettings %q: %#v", d.Id(), res) + + return resourceLoggingFolderSettingsRead(d, meta) +} + +func resourceLoggingFolderSettingsRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LoggingBasePath}}folders/{{folder}}/settings") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("LoggingFolderSettings %q", d.Id())) + } + + if err := d.Set("name", flattenLoggingFolderSettingsName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading FolderSettings: %s", err) + } + if err := d.Set("kms_key_name", flattenLoggingFolderSettingsKmsKeyName(res["kmsKeyName"], d, config)); err != nil { + return fmt.Errorf("Error reading FolderSettings: %s", err) + } + if err := d.Set("kms_service_account_id", flattenLoggingFolderSettingsKmsServiceAccountId(res["kmsServiceAccountId"], d, config)); err != nil { + return fmt.Errorf("Error reading FolderSettings: %s", err) + } + if err := d.Set("storage_location", flattenLoggingFolderSettingsStorageLocation(res["storageLocation"], d, config)); err != nil { + return fmt.Errorf("Error reading FolderSettings: %s", err) + } + if err := d.Set("disable_default_sink", flattenLoggingFolderSettingsDisableDefaultSink(res["disableDefaultSink"], d, config)); err != nil { + return fmt.Errorf("Error reading FolderSettings: %s", err) + } + if err := d.Set("logging_service_account_id", flattenLoggingFolderSettingsLoggingServiceAccountId(res["loggingServiceAccountId"], d, config)); err != nil { + return fmt.Errorf("Error reading FolderSettings: %s", err) + } + + return nil +} + +func resourceLoggingFolderSettingsUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + obj := make(map[string]interface{}) + kmsKeyNameProp, err := expandLoggingFolderSettingsKmsKeyName(d.Get("kms_key_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("kms_key_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) { + obj["kmsKeyName"] = kmsKeyNameProp + } + storageLocationProp, err := expandLoggingFolderSettingsStorageLocation(d.Get("storage_location"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("storage_location"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, storageLocationProp)) { + obj["storageLocation"] = storageLocationProp + } + disableDefaultSinkProp, err := expandLoggingFolderSettingsDisableDefaultSink(d.Get("disable_default_sink"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("disable_default_sink"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, disableDefaultSinkProp)) { + obj["disableDefaultSink"] = disableDefaultSinkProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LoggingBasePath}}folders/{{folder}}/settings?updateMask=disableDefaultSink,storageLocation,kmsKeyName") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating FolderSettings %q: %#v", d.Id(), obj) + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + + if err != nil { + return fmt.Errorf("Error updating FolderSettings %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating FolderSettings %q: %#v", d.Id(), res) + } + + return resourceLoggingFolderSettingsRead(d, meta) +} + +func resourceLoggingFolderSettingsDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[WARNING] Logging FolderSettings resources"+ + " cannot be deleted from Google Cloud. The resource %s will be removed from Terraform"+ + " state, but will still be present on Google Cloud.", d.Id()) + d.SetId("") + + return nil +} + +func resourceLoggingFolderSettingsImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "^folders/(?P[^/]+)/settings$", + "^(?P[^/]+)$", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "folders/{{folder}}/settings") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenLoggingFolderSettingsName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingFolderSettingsKmsKeyName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingFolderSettingsKmsServiceAccountId(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingFolderSettingsStorageLocation(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingFolderSettingsDisableDefaultSink(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingFolderSettingsLoggingServiceAccountId(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func expandLoggingFolderSettingsKmsKeyName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLoggingFolderSettingsStorageLocation(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLoggingFolderSettingsDisableDefaultSink(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} diff --git a/google-beta/services/logging/resource_logging_folder_settings_generated_test.go b/google-beta/services/logging/resource_logging_folder_settings_generated_test.go new file mode 100644 index 0000000000..77017ebc4f --- /dev/null +++ b/google-beta/services/logging/resource_logging_folder_settings_generated_test.go @@ -0,0 +1,80 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// 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 logging_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar" +) + +func TestAccLoggingFolderSettings_loggingFolderSettingsAllExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "key_name": acctest.BootstrapKMSKeyInLocation(t, "us-central1").CryptoKey.Name, + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccLoggingFolderSettings_loggingFolderSettingsAllExample(context), + }, + { + ResourceName: "google_logging_folder_settings.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"folder"}, + }, + }, + }) +} + +func testAccLoggingFolderSettings_loggingFolderSettingsAllExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_logging_folder_settings" "example" { + disable_default_sink = true + folder = google_folder.my_folder.folder_id + kms_key_name = "%{key_name}" + storage_location = "us-central1" + depends_on = [ google_kms_crypto_key_iam_member.iam ] +} + +resource "google_folder" "my_folder" { + display_name = "tf-test-folder-name%{random_suffix}" + parent = "organizations/%{org_id}" +} + +data "google_logging_folder_settings" "settings" { + folder = google_folder.my_folder.folder_id +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = "%{key_name}" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:${data.google_logging_folder_settings.settings.kms_service_account_id}" +} +`, context) +} diff --git a/google-beta/services/logging/resource_logging_folder_settings_test.go b/google-beta/services/logging/resource_logging_folder_settings_test.go new file mode 100644 index 0000000000..55a8b39b41 --- /dev/null +++ b/google-beta/services/logging/resource_logging_folder_settings_test.go @@ -0,0 +1,88 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package logging_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar" +) + +func TestAccLoggingFolderSettings_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + "original_key": acctest.BootstrapKMSKeyInLocation(t, "us-central1").CryptoKey.Name, + "updated_key": acctest.BootstrapKMSKeyInLocation(t, "us-east1").CryptoKey.Name, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccLoggingFolderSettings_onlyRequired(context), + }, + { + ResourceName: "google_logging_folder_settings.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"folder"}, + }, + { + Config: testAccLoggingFolderSettings_full(context), + }, + { + ResourceName: "google_logging_folder_settings.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"folder"}, + }, + }, + }) +} + +func testAccLoggingFolderSettings_full(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_logging_folder_settings" "example" { + disable_default_sink = true + folder = google_folder.my_folder.folder_id + kms_key_name = "%{original_key}" + storage_location = "us-central1" + depends_on = [ google_kms_crypto_key_iam_member.iam ] +} + +resource "google_folder" "my_folder" { + display_name = "tf-test-folder-%{random_suffix}" + parent = "organizations/%{org_id}" +} + +data "google_logging_folder_settings" "settings" { + folder = google_folder.my_folder.folder_id +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = "%{original_key}" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:${data.google_logging_folder_settings.settings.kms_service_account_id}" +} +`, context) +} + +func testAccLoggingFolderSettings_onlyRequired(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_logging_folder_settings" "example" { + folder = google_folder.my_folder.folder_id +} + +resource "google_folder" "my_folder" { + display_name = "tf-test-folder-%{random_suffix}" + parent = "organizations/%{org_id}" +} +`, context) +} diff --git a/google-beta/services/logging/resource_logging_organization_settings.go b/google-beta/services/logging/resource_logging_organization_settings.go new file mode 100644 index 0000000000..209d4bad4b --- /dev/null +++ b/google-beta/services/logging/resource_logging_organization_settings.go @@ -0,0 +1,336 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// 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 logging + +import ( + "fmt" + "log" + "reflect" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" +) + +func ResourceLoggingOrganizationSettings() *schema.Resource { + return &schema.Resource{ + Create: resourceLoggingOrganizationSettingsCreate, + Read: resourceLoggingOrganizationSettingsRead, + Update: resourceLoggingOrganizationSettingsUpdate, + Delete: resourceLoggingOrganizationSettingsDelete, + + Importer: &schema.ResourceImporter{ + State: resourceLoggingOrganizationSettingsImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "organization": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The organization for which to retrieve or configure settings.`, + }, + "disable_default_sink": { + Type: schema.TypeBool, + Computed: true, + Optional: true, + Description: `If set to true, the _Default sink in newly created projects and folders will created in a disabled state. This can be used to automatically disable log storage if there is already an aggregated sink configured in the hierarchy. The _Default sink can be re-enabled manually if needed.`, + }, + "kms_key_name": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `The resource name for the configured Cloud KMS key.`, + }, + "storage_location": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Description: `The storage location that Cloud Logging will use to create new resources when a location is needed but not explicitly provided.`, + }, + "kms_service_account_id": { + Type: schema.TypeString, + Computed: true, + Description: `The service account that will be used by the Log Router to access your Cloud KMS key.`, + }, + "logging_service_account_id": { + Type: schema.TypeString, + Computed: true, + Description: `The service account for the given container. Sinks use this service account as their writerIdentity if no custom service account is provided.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The resource name of the settings.`, + }, + }, + UseJSONNumber: true, + } +} + +func resourceLoggingOrganizationSettingsCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + kmsKeyNameProp, err := expandLoggingOrganizationSettingsKmsKeyName(d.Get("kms_key_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("kms_key_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(kmsKeyNameProp)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) { + obj["kmsKeyName"] = kmsKeyNameProp + } + storageLocationProp, err := expandLoggingOrganizationSettingsStorageLocation(d.Get("storage_location"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("storage_location"); !tpgresource.IsEmptyValue(reflect.ValueOf(storageLocationProp)) && (ok || !reflect.DeepEqual(v, storageLocationProp)) { + obj["storageLocation"] = storageLocationProp + } + disableDefaultSinkProp, err := expandLoggingOrganizationSettingsDisableDefaultSink(d.Get("disable_default_sink"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("disable_default_sink"); !tpgresource.IsEmptyValue(reflect.ValueOf(disableDefaultSinkProp)) && (ok || !reflect.DeepEqual(v, disableDefaultSinkProp)) { + obj["disableDefaultSink"] = disableDefaultSinkProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LoggingBasePath}}organizations/{{organization}}/settings?updateMask=disableDefaultSink,storageLocation,kmsKeyName") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new OrganizationSettings: %#v", obj) + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + }) + if err != nil { + return fmt.Errorf("Error creating OrganizationSettings: %s", err) + } + if err := d.Set("name", flattenLoggingOrganizationSettingsName(res["name"], d, config)); err != nil { + return fmt.Errorf(`Error setting computed identity field "name": %s`, err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "organizations/{{organization}}/settings") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating OrganizationSettings %q: %#v", d.Id(), res) + + return resourceLoggingOrganizationSettingsRead(d, meta) +} + +func resourceLoggingOrganizationSettingsRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LoggingBasePath}}organizations/{{organization}}/settings") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("LoggingOrganizationSettings %q", d.Id())) + } + + if err := d.Set("name", flattenLoggingOrganizationSettingsName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading OrganizationSettings: %s", err) + } + if err := d.Set("kms_key_name", flattenLoggingOrganizationSettingsKmsKeyName(res["kmsKeyName"], d, config)); err != nil { + return fmt.Errorf("Error reading OrganizationSettings: %s", err) + } + if err := d.Set("kms_service_account_id", flattenLoggingOrganizationSettingsKmsServiceAccountId(res["kmsServiceAccountId"], d, config)); err != nil { + return fmt.Errorf("Error reading OrganizationSettings: %s", err) + } + if err := d.Set("storage_location", flattenLoggingOrganizationSettingsStorageLocation(res["storageLocation"], d, config)); err != nil { + return fmt.Errorf("Error reading OrganizationSettings: %s", err) + } + if err := d.Set("disable_default_sink", flattenLoggingOrganizationSettingsDisableDefaultSink(res["disableDefaultSink"], d, config)); err != nil { + return fmt.Errorf("Error reading OrganizationSettings: %s", err) + } + if err := d.Set("logging_service_account_id", flattenLoggingOrganizationSettingsLoggingServiceAccountId(res["loggingServiceAccountId"], d, config)); err != nil { + return fmt.Errorf("Error reading OrganizationSettings: %s", err) + } + + return nil +} + +func resourceLoggingOrganizationSettingsUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + obj := make(map[string]interface{}) + kmsKeyNameProp, err := expandLoggingOrganizationSettingsKmsKeyName(d.Get("kms_key_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("kms_key_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) { + obj["kmsKeyName"] = kmsKeyNameProp + } + storageLocationProp, err := expandLoggingOrganizationSettingsStorageLocation(d.Get("storage_location"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("storage_location"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, storageLocationProp)) { + obj["storageLocation"] = storageLocationProp + } + disableDefaultSinkProp, err := expandLoggingOrganizationSettingsDisableDefaultSink(d.Get("disable_default_sink"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("disable_default_sink"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, disableDefaultSinkProp)) { + obj["disableDefaultSink"] = disableDefaultSinkProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LoggingBasePath}}organizations/{{organization}}/settings?updateMask=disableDefaultSink,storageLocation,kmsKeyName") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating OrganizationSettings %q: %#v", d.Id(), obj) + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + + if err != nil { + return fmt.Errorf("Error updating OrganizationSettings %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating OrganizationSettings %q: %#v", d.Id(), res) + } + + return resourceLoggingOrganizationSettingsRead(d, meta) +} + +func resourceLoggingOrganizationSettingsDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[WARNING] Logging OrganizationSettings resources"+ + " cannot be deleted from Google Cloud. The resource %s will be removed from Terraform"+ + " state, but will still be present on Google Cloud.", d.Id()) + d.SetId("") + + return nil +} + +func resourceLoggingOrganizationSettingsImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "^organizations/(?P[^/]+)/settings$", + "^(?P[^/]+)$", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "organizations/{{organization}}/settings") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenLoggingOrganizationSettingsName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingOrganizationSettingsKmsKeyName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingOrganizationSettingsKmsServiceAccountId(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingOrganizationSettingsStorageLocation(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingOrganizationSettingsDisableDefaultSink(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLoggingOrganizationSettingsLoggingServiceAccountId(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func expandLoggingOrganizationSettingsKmsKeyName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLoggingOrganizationSettingsStorageLocation(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLoggingOrganizationSettingsDisableDefaultSink(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} diff --git a/google-beta/services/logging/resource_logging_organization_settings_test.go b/google-beta/services/logging/resource_logging_organization_settings_test.go new file mode 100644 index 0000000000..a35c9b6cee --- /dev/null +++ b/google-beta/services/logging/resource_logging_organization_settings_test.go @@ -0,0 +1,76 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package logging_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google-beta/google-beta/acctest" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/envvar" +) + +func TestAccLoggingOrganizationSettings_update(t *testing.T) { + context := map[string]interface{}{ + "org_id": envvar.GetTestOrgTargetFromEnv(t), + "random_suffix": acctest.RandString(t, 10), + "original_key": acctest.BootstrapKMSKeyInLocation(t, "us-central1").CryptoKey.Name, + "updated_key": acctest.BootstrapKMSKeyInLocation(t, "us-east1").CryptoKey.Name, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccLoggingOrganizationSettings_onlyRequired(context), + }, + { + ResourceName: "google_logging_organization_settings.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"organization"}, + }, + { + Config: testAccLoggingOrganizationSettings_full(context), + }, + { + ResourceName: "google_logging_organization_settings.example", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"organization"}, + }, + }, + }) +} + +func testAccLoggingOrganizationSettings_full(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_logging_organization_settings" "example" { + disable_default_sink = false + kms_key_name = "%{original_key}" + organization = "%{org_id}" + storage_location = "us-central1" + depends_on = [ google_kms_crypto_key_iam_member.iam ] +} + +data "google_logging_organization_settings" "settings" { + organization = "%{org_id}" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = "%{original_key}" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:${data.google_logging_organization_settings.settings.kms_service_account_id}" +} +`, context) +} + +func testAccLoggingOrganizationSettings_onlyRequired(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_logging_organization_settings" "example" { + organization = "%{org_id}" +} +`, context) +} diff --git a/website/docs/r/logging_folder_settings.html.markdown b/website/docs/r/logging_folder_settings.html.markdown new file mode 100644 index 0000000000..882938efa5 --- /dev/null +++ b/website/docs/r/logging_folder_settings.html.markdown @@ -0,0 +1,133 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# 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. +# +# ---------------------------------------------------------------------------- +subcategory: "Cloud (Stackdriver) Logging" +description: |- + Default resource settings control whether CMEK is required for new log buckets. +--- + +# google\_logging\_folder\_settings + +Default resource settings control whether CMEK is required for new log buckets. These settings also determine the storage location for the _Default and _Required log buckets, and whether the _Default sink is enabled or disabled. + + +To get more information about FolderSettings, see: + +* [API documentation](https://cloud.google.com/logging/docs/reference/v2/rest/v2/TopLevel/getSettings) +* How-to Guides + * [Configure default settings for organizations and folders](https://cloud.google.com/logging/docs/default-settings) + +## Example Usage - Logging Folder Settings All + + +```hcl +resource "google_logging_folder_settings" "example" { + disable_default_sink = true + folder = google_folder.my_folder.folder_id + kms_key_name = "kms-key" + storage_location = "us-central1" + depends_on = [ google_kms_crypto_key_iam_member.iam ] +} + +resource "google_folder" "my_folder" { + display_name = "folder-name" + parent = "organizations/123456789" +} + +data "google_logging_folder_settings" "settings" { + folder = google_folder.my_folder.folder_id +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = "kms-key" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:${data.google_logging_folder_settings.settings.kms_service_account_id}" +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `folder` - + (Required) + The folder for which to retrieve settings. + + +- - - + + +* `kms_key_name` - + (Optional) + The resource name for the configured Cloud KMS key. + +* `storage_location` - + (Optional) + The storage location that Cloud Logging will use to create new resources when a location is needed but not explicitly provided. + +* `disable_default_sink` - + (Optional) + If set to true, the _Default sink in newly created projects and folders will created in a disabled state. This can be used to automatically disable log storage if there is already an aggregated sink configured in the hierarchy. The _Default sink can be re-enabled manually if needed. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `folders/{{folder}}/settings` + +* `name` - + The resource name of the settings. + +* `kms_service_account_id` - + The service account that will be used by the Log Router to access your Cloud KMS key. + +* `logging_service_account_id` - + The service account for the given container. Sinks use this service account as their writerIdentity if no custom service account is provided. + + +## Timeouts + +This resource provides the following +[Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +FolderSettings can be imported using any of these accepted formats: + +* `folders/{{folder}}/settings` +* `{{folder}}` + + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import FolderSettings using one of the formats above. For example: + +```tf +import { + id = "folders/{{folder}}/settings" + to = google_logging_folder_settings.default +} +``` + +When using the [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import), FolderSettings can be imported using one of the formats above. For example: + +``` +$ terraform import google_logging_folder_settings.default folders/{{folder}}/settings +$ terraform import google_logging_folder_settings.default {{folder}} +``` diff --git a/website/docs/r/logging_organization_settings.html.markdown b/website/docs/r/logging_organization_settings.html.markdown new file mode 100644 index 0000000000..64b9dee509 --- /dev/null +++ b/website/docs/r/logging_organization_settings.html.markdown @@ -0,0 +1,128 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# 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. +# +# ---------------------------------------------------------------------------- +subcategory: "Cloud (Stackdriver) Logging" +description: |- + Default resource settings control whether CMEK is required for new log buckets. +--- + +# google\_logging\_organization\_settings + +Default resource settings control whether CMEK is required for new log buckets. These settings also determine the storage location for the _Default and _Required log buckets, and whether the _Default sink is enabled or disabled. + + +To get more information about OrganizationSettings, see: + +* [API documentation](https://cloud.google.com/logging/docs/reference/v2/rest/v2/TopLevel/getSettings) +* How-to Guides + * [Configure default settings for organizations and folders](https://cloud.google.com/logging/docs/default-settings) + +## Example Usage - Logging Organization Settings All + + +```hcl +resource "google_logging_organization_settings" "example" { + disable_default_sink = true + kms_key_name = "kms-key" + organization = "123456789" + storage_location = "us-central1" + depends_on = [ google_kms_crypto_key_iam_member.iam ] +} + +data "google_logging_organization_settings" "settings" { + organization = "123456789" +} + +resource "google_kms_crypto_key_iam_member" "iam" { + crypto_key_id = "kms-key" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:${data.google_logging_organization_settings.settings.kms_service_account_id}" +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `organization` - + (Required) + The organization for which to retrieve or configure settings. + + +- - - + + +* `kms_key_name` - + (Optional) + The resource name for the configured Cloud KMS key. + +* `storage_location` - + (Optional) + The storage location that Cloud Logging will use to create new resources when a location is needed but not explicitly provided. + +* `disable_default_sink` - + (Optional) + If set to true, the _Default sink in newly created projects and folders will created in a disabled state. This can be used to automatically disable log storage if there is already an aggregated sink configured in the hierarchy. The _Default sink can be re-enabled manually if needed. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `organizations/{{organization}}/settings` + +* `name` - + The resource name of the settings. + +* `kms_service_account_id` - + The service account that will be used by the Log Router to access your Cloud KMS key. + +* `logging_service_account_id` - + The service account for the given container. Sinks use this service account as their writerIdentity if no custom service account is provided. + + +## Timeouts + +This resource provides the following +[Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +OrganizationSettings can be imported using any of these accepted formats: + +* `organizations/{{organization}}/settings` +* `{{organization}}` + + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import OrganizationSettings using one of the formats above. For example: + +```tf +import { + id = "organizations/{{organization}}/settings" + to = google_logging_organization_settings.default +} +``` + +When using the [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import), OrganizationSettings can be imported using one of the formats above. For example: + +``` +$ terraform import google_logging_organization_settings.default organizations/{{organization}}/settings +$ terraform import google_logging_organization_settings.default {{organization}} +```