From 4db22b5173f8c11a894e8b719ce594ea50cdc639 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 03:28:38 +0000 Subject: [PATCH 1/3] feat: Add converter for logging_organization_sink This commit adds a new converter to convert `google_logging_organization_sink` Terraform plans to the Cloud Asset Inventory (CAI) format. The converter handles the following fields: - name - destination - filter - description - disabled - exclusions - include_children - bigquery_options --- mmv1/provider/terraform_tgc.go | 1 + .../tgc/resource_converters.go.tmpl | 1 + .../logging/logging_organization_sink.go | 209 ++++++++++++++++++ .../tests/data/logging_organization_sink.json | 86 +++++++ .../tests/data/logging_organization_sink.tf | 38 ++++ 5 files changed, 335 insertions(+) create mode 100644 mmv1/third_party/tgc/services/logging/logging_organization_sink.go create mode 100644 mmv1/third_party/tgc/tests/data/logging_organization_sink.json create mode 100644 mmv1/third_party/tgc/tests/data/logging_organization_sink.tf diff --git a/mmv1/provider/terraform_tgc.go b/mmv1/provider/terraform_tgc.go index 9489d077015d..cc66d188e3c4 100644 --- a/mmv1/provider/terraform_tgc.go +++ b/mmv1/provider/terraform_tgc.go @@ -435,6 +435,7 @@ func (tgc TerraformGoogleConversion) CopyCommonFiles(outputFolder string, genera "converters/google/resources/services/logging/logging_billing_account_bucket_config.go": "third_party/tgc/services/logging/logging_billing_account_bucket_config.go", "converters/google/resources/services/appengine/appengine_standard_version.go": "third_party/tgc/services/appengine/appengine_standard_version.go", "converters/google/resources/services/logging/logging_project_sink.go": "third_party/tgc/services/logging/logging_project_sink.go", + "converters/google/resources/services/logging/logging_organization_sink.go": "third_party/tgc/services/logging/logging_organization_sink.go", } tgc.CopyFileList(outputFolder, resourceConverters) } diff --git a/mmv1/third_party/tgc/resource_converters.go.tmpl b/mmv1/third_party/tgc/resource_converters.go.tmpl index bfaafa3512a4..b97c89f0a06b 100644 --- a/mmv1/third_party/tgc/resource_converters.go.tmpl +++ b/mmv1/third_party/tgc/resource_converters.go.tmpl @@ -139,6 +139,7 @@ func ResourceConverters() map[string][]cai.ResourceConverter { "google_logging_project_bucket_config": {logging.ResourceConverterLogProjectBucket()}, "google_logging_billing_account_bucket_config": {logging.ResourceConverterLogBillingAccountBucket()}, "google_logging_project_sink": {logging.ResourceConverterLogProjectSink()}, + "google_logging_organization_sink": {logging.ResourceConverterLogOrganizationSink()}, "google_cloud_tasks_queue": {cloudtasks.ResourceConverterCloudTasksQueue()}, "google_pubsub_topic": {pubsub.ResourceConverterPubsubTopic()}, "google_kms_crypto_key": {kms.ResourceConverterKMSCryptoKey()}, diff --git a/mmv1/third_party/tgc/services/logging/logging_organization_sink.go b/mmv1/third_party/tgc/services/logging/logging_organization_sink.go new file mode 100644 index 000000000000..a195b444eaff --- /dev/null +++ b/mmv1/third_party/tgc/services/logging/logging_organization_sink.go @@ -0,0 +1,209 @@ +package logging + +import ( + "reflect" + + "github.com/GoogleCloudPlatform/terraform-google-conversion/v7/tfplan2cai/converters/google/resources/cai" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" +) + +func ResourceConverterLogOrganizationSink() cai.ResourceConverter { + return cai.ResourceConverter{ + AssetType: logSinkAssetType, + Convert: GetLogOrganizationSinkCaiObject, + } +} + +func GetLogOrganizationSinkCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]cai.Asset, error) { + name, err := cai.AssetName(d, config, "//logging.googleapis.com/organizations/{{org_id}}/sinks/{{name}}") + if err != nil { + return []cai.Asset{}, err + } + obj, err := GetLogOrganizationSinkApiObject(d, config) + if err != nil { + return []cai.Asset{}, err + } + return []cai.Asset{{ + Name: name, + Type: logSinkAssetType, + Resource: &cai.AssetResource{ + Version: "v2", + DiscoveryDocumentURI: "https://logging.googleapis.com/$discovery/rest?version=v2", + DiscoveryName: "LogSink", + Data: obj, + }, + }}, nil +} + +func GetLogOrganizationSinkApiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]interface{}, error) { + obj := make(map[string]interface{}) + + nameProp, err := expandLogOrganizationSinkName(d.Get("name"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("name"); !tpgresource.IsEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp + } + + destinationProp, err := expandLogOrganizationSinkDestination(d.Get("destination"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("destination"); !tpgresource.IsEmptyValue(reflect.ValueOf(destinationProp)) && (ok || !reflect.DeepEqual(v, destinationProp)) { + obj["destination"] = destinationProp + } + + filterProp, err := expandLogOrganizationSinkFilter(d.Get("filter"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("filter"); !tpgresource.IsEmptyValue(reflect.ValueOf(filterProp)) && (ok || !reflect.DeepEqual(v, filterProp)) { + obj["filter"] = filterProp + } + + descriptionProp, err := expandLogOrganizationSinkDescription(d.Get("description"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("description"); !tpgresource.IsEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + + disabledProp, err := expandLogOrganizationSinkDisabled(d.Get("disabled"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("disabled"); !tpgresource.IsEmptyValue(reflect.ValueOf(disabledProp)) && (ok || !reflect.DeepEqual(v, disabledProp)) { + obj["disabled"] = disabledProp + } + + exclusionsProp, err := expandLogOrganizationSinkExclusions(d.Get("exclusions"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("exclusions"); !tpgresource.IsEmptyValue(reflect.ValueOf(exclusionsProp)) && (ok || !reflect.DeepEqual(v, exclusionsProp)) { + obj["exclusions"] = exclusionsProp + } + + includeChildrenProp, err := expandLogOrganizationSinkIncludeChildren(d.Get("include_children"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("include_children"); !tpgresource.IsEmptyValue(reflect.ValueOf(includeChildrenProp)) && (ok || !reflect.DeepEqual(v, includeChildrenProp)) { + obj["includeChildren"] = includeChildrenProp + } + + bigqueryOptionsProp, err := expandLogOrganizationSinkBigqueryOptions(d.Get("bigquery_options"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("bigquery_options"); !tpgresource.IsEmptyValue(reflect.ValueOf(bigqueryOptionsProp)) && (ok || !reflect.DeepEqual(v, bigqueryOptionsProp)) { + obj["bigqueryOptions"] = bigqueryOptionsProp + } + + return obj, nil +} + +func expandLogOrganizationSinkName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkDestination(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkFilter(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkDescription(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkDisabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkIncludeChildren(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkExclusions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l, ok := v.([]interface{}) + if !ok { + return nil, nil + } + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedName, err := expandLogOrganizationSinkExclusionsName(original["name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedName); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["name"] = transformedName + } + + transformedDescription, err := expandLogOrganizationSinkExclusionsDescription(original["description"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["description"] = transformedDescription + } + + transformedFilter, err := expandLogOrganizationSinkExclusionsFilter(original["filter"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFilter); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["filter"] = transformedFilter + } + + transformedDisabled, err := expandLogOrganizationSinkExclusionsDisabled(original["disabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["disabled"] = transformedDisabled + } + + req = append(req, transformed) + } + + return req, nil +} + +func expandLogOrganizationSinkExclusionsName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkExclusionsDescription(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkExclusionsFilter(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkExclusionsDisabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLogOrganizationSinkBigqueryOptions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedUsePartitionedTables, err := expandLogOrganizationSinkBigqueryOptionsUsePartitionedTables(original["use_partitioned_tables"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedUsePartitionedTables); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["usePartitionedTables"] = transformedUsePartitionedTables + } + + return transformed, nil +} + +func expandLogOrganizationSinkBigqueryOptionsUsePartitionedTables(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} diff --git a/mmv1/third_party/tgc/tests/data/logging_organization_sink.json b/mmv1/third_party/tgc/tests/data/logging_organization_sink.json new file mode 100644 index 000000000000..43c7fb98a58e --- /dev/null +++ b/mmv1/third_party/tgc/tests/data/logging_organization_sink.json @@ -0,0 +1,86 @@ +[ + { + "name": "//logging.googleapis.com/organizations/{{.OrgID}}/sinks/gg-asset-88093-71a3-sink", + "asset_type": "logging.googleapis.com/LogSink", + "ancestry_path": "organizations/{{.OrgID}}", + "resource": { + "version": "v2", + "discovery_document_uri": "https://logging.googleapis.com/$discovery/rest", + "discovery_name": "LogSink", + "parent": "//cloudresourcemanager.googleapis.com/organizations/{{.OrgID}}", + "data": { + "destination": "bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/my_dataset", + "exclusions": [ + { + "description": "Exclude all GCE instance logs", + "disabled": true, + "filter": "resource.type = gce_instance", + "name": "gg-asset-88093-71a3-exclusion" + } + ], + "name": "gg-asset-88093-71a3-sink" + } + }, + "ancestors": [ + "organizations/{{.OrgID}}" + ] + }, + { + "name": "//logging.googleapis.com/organizations/{{.OrgID}}/sinks/gg-asset-88093-71a3-sink-with-children", + "asset_type": "logging.googleapis.com/LogSink", + "ancestry_path": "organizations/{{.OrgID}}", + "resource": { + "version": "v2", + "discovery_document_uri": "https://logging.googleapis.com/$discovery/rest", + "discovery_name": "LogSink", + "parent": "//cloudresourcemanager.googleapis.com/organizations/{{.OrgID}}", + "data": { + "destination": "bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/my_dataset", + "exclusions": [ + { + "description": "Exclude all GCE instance logs", + "disabled": true, + "filter": "resource.type = gce_instance", + "name": "gg-asset-88093-71a3-exclusion" + } + ], + "includeChildren": true, + "name": "gg-asset-88093-71a3-sink-with-children" + } + }, + "ancestors": [ + "organizations/{{.OrgID}}" + ] + }, + { + "name": "//bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/my_dataset", + "asset_type": "bigquery.googleapis.com/Dataset", + "ancestry_path": "{{.Ancestry}}/project/{{.Provider.project}}", + "resource": { + "version": "v2", + "discovery_document_uri": "https://www.googleapis.com/discovery/v1/apis/bigquery/v2/rest", + "discovery_name": "Dataset", + "parent": "//cloudresourcemanager.googleapis.com/projects/{{.Provider.project}}", + "data": { + "datasetReference": { + "datasetId": "my_dataset" + }, + "friendlyName": "", + "labels": { + "goog-terraform-provisioned": "true" + }, + "location": "US" + } + }, + "iam_policy": { + "bindings": [ + { + "role": "roles/bigquery.dataEditor", + "members": [ + "" + ] + } + ] + } + } +] \ No newline at end of file diff --git a/mmv1/third_party/tgc/tests/data/logging_organization_sink.tf b/mmv1/third_party/tgc/tests/data/logging_organization_sink.tf new file mode 100644 index 000000000000..435ffd2d40f9 --- /dev/null +++ b/mmv1/third_party/tgc/tests/data/logging_organization_sink.tf @@ -0,0 +1,38 @@ +resource "google_bigquery_dataset" "my_dataset" { + project = "{{.Provider.project}}" + dataset_id = "my_dataset" + location = "US" +} + +resource "google_logging_organization_sink" "my_sink" { + name = "gg-asset-88093-71a3-sink" + org_id = "{{.OrgID}}" + destination = "bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/${google_bigquery_dataset.my_dataset.dataset_id}" + include_children = false + exclusions { + name = "gg-asset-88093-71a3-exclusion" + description = "Exclude all GCE instance logs" + filter = "resource.type = gce_instance" + disabled = true + } +} + +resource "google_bigquery_dataset_iam_member" "my_iam" { + project = "{{.Provider.project}}" + dataset_id = google_bigquery_dataset.my_dataset.dataset_id + role = "roles/bigquery.dataEditor" + member = google_logging_organization_sink.my_sink.writer_identity +} + +resource "google_logging_organization_sink" "my_sink_with_children" { + name = "gg-asset-88093-71a3-sink-with-children" + org_id = "{{.OrgID}}" + destination = "bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/${google_bigquery_dataset.my_dataset.dataset_id}" + include_children = true + exclusions { + name = "gg-asset-88093-71a3-exclusion" + description = "Exclude all GCE instance logs" + filter = "resource.type = gce_instance" + disabled = true + } +} From 3096d692e21893073928385bc67d2287bf225b60 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 05:59:34 +0000 Subject: [PATCH 2/3] feat: Add converter for logging_organization_sink This commit adds a new converter to convert `google_logging_organization_sink` Terraform plans to the Cloud Asset Inventory (CAI) format. The converter handles the following fields: - name - destination - filter - description - disabled - exclusions - include_children - intercept_children - bigquery_options --- .../logging/logging_organization_sink.go | 11 ++++++++ .../tests/data/logging_organization_sink.json | 27 +++++++++++++++++++ .../tests/data/logging_organization_sink.tf | 13 +++++++++ 3 files changed, 51 insertions(+) diff --git a/mmv1/third_party/tgc/services/logging/logging_organization_sink.go b/mmv1/third_party/tgc/services/logging/logging_organization_sink.go index a195b444eaff..2b583e420a7f 100644 --- a/mmv1/third_party/tgc/services/logging/logging_organization_sink.go +++ b/mmv1/third_party/tgc/services/logging/logging_organization_sink.go @@ -88,6 +88,13 @@ func GetLogOrganizationSinkApiObject(d tpgresource.TerraformResourceData, config obj["includeChildren"] = includeChildrenProp } + interceptChildrenProp, err := expandLogOrganizationSinkInterceptChildren(d.Get("intercept_children"), d, config) + if err != nil { + return nil, err + } else if v, ok := d.GetOkExists("intercept_children"); !tpgresource.IsEmptyValue(reflect.ValueOf(interceptChildrenProp)) && (ok || !reflect.DeepEqual(v, interceptChildrenProp)) { + obj["interceptChildren"] = interceptChildrenProp + } + bigqueryOptionsProp, err := expandLogOrganizationSinkBigqueryOptions(d.Get("bigquery_options"), d, config) if err != nil { return nil, err @@ -122,6 +129,10 @@ func expandLogOrganizationSinkIncludeChildren(v interface{}, d tpgresource.Terra return v, nil } +func expandLogOrganizationSinkInterceptChildren(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + func expandLogOrganizationSinkExclusions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { l, ok := v.([]interface{}) if !ok { diff --git a/mmv1/third_party/tgc/tests/data/logging_organization_sink.json b/mmv1/third_party/tgc/tests/data/logging_organization_sink.json index 43c7fb98a58e..ba5602511e1a 100644 --- a/mmv1/third_party/tgc/tests/data/logging_organization_sink.json +++ b/mmv1/third_party/tgc/tests/data/logging_organization_sink.json @@ -52,6 +52,33 @@ "organizations/{{.OrgID}}" ] }, + { + "name": "//logging.googleapis.com/organizations/{{.OrgID}}/sinks/gg-asset-88093-71a3-sink-with-intercept", + "asset_type": "logging.googleapis.com/LogSink", + "ancestry_path": "organizations/{{.OrgID}}", + "resource": { + "version": "v2", + "discovery_document_uri": "https://logging.googleapis.com/$discovery/rest", + "discovery_name": "LogSink", + "parent": "//cloudresourcemanager.googleapis.com/organizations/{{.OrgID}}", + "data": { + "destination": "bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/my_dataset", + "exclusions": [ + { + "description": "Exclude all GCE instance logs", + "disabled": true, + "filter": "resource.type = gce_instance", + "name": "gg-asset-88093-71a3-exclusion" + } + ], + "interceptChildren": true, + "name": "gg-asset-88093-71a3-sink-with-intercept" + } + }, + "ancestors": [ + "organizations/{{.OrgID}}" + ] + }, { "name": "//bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/my_dataset", "asset_type": "bigquery.googleapis.com/Dataset", diff --git a/mmv1/third_party/tgc/tests/data/logging_organization_sink.tf b/mmv1/third_party/tgc/tests/data/logging_organization_sink.tf index 435ffd2d40f9..527874c44b67 100644 --- a/mmv1/third_party/tgc/tests/data/logging_organization_sink.tf +++ b/mmv1/third_party/tgc/tests/data/logging_organization_sink.tf @@ -17,6 +17,19 @@ resource "google_logging_organization_sink" "my_sink" { } } +resource "google_logging_organization_sink" "my_sink_with_intercept" { + name = "gg-asset-88093-71a3-sink-with-intercept" + org_id = "{{.OrgID}}" + destination = "bigquery.googleapis.com/projects/{{.Provider.project}}/datasets/${google_bigquery_dataset.my_dataset.dataset_id}" + intercept_children = true + exclusions { + name = "gg-asset-88093-71a3-exclusion" + description = "Exclude all GCE instance logs" + filter = "resource.type = gce_instance" + disabled = true + } +} + resource "google_bigquery_dataset_iam_member" "my_iam" { project = "{{.Provider.project}}" dataset_id = google_bigquery_dataset.my_dataset.dataset_id From f431e4e57b6897d9f88dfc84ce120a8c14323d23 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:50:22 +0000 Subject: [PATCH 3/3] feat: Add converter for logging_organization_sink This commit adds a new converter to convert `google_logging_organization_sink` Terraform plans to the Cloud Asset Inventory (CAI) format. The converter handles the following fields: - name - destination - filter - description - disabled - exclusions - include_children - intercept_children - bigquery_options --- .../tgc/services/logging/logging_organization_sink.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmv1/third_party/tgc/services/logging/logging_organization_sink.go b/mmv1/third_party/tgc/services/logging/logging_organization_sink.go index 2b583e420a7f..a2cb97343c6b 100644 --- a/mmv1/third_party/tgc/services/logging/logging_organization_sink.go +++ b/mmv1/third_party/tgc/services/logging/logging_organization_sink.go @@ -29,7 +29,7 @@ func GetLogOrganizationSinkCaiObject(d tpgresource.TerraformResourceData, config Type: logSinkAssetType, Resource: &cai.AssetResource{ Version: "v2", - DiscoveryDocumentURI: "https://logging.googleapis.com/$discovery/rest?version=v2", + DiscoveryDocumentURI: "https://logging.googleapis.com/$discovery/rest", DiscoveryName: "LogSink", Data: obj, },