Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
22a3f54
Add datasource for observability project settings
leowonderful Jan 27, 2026
d880a29
add required wait to test
leowonderful Jan 30, 2026
0e7dea0
remove retry logic from datasource
leowonderful Jan 30, 2026
0d4da0b
reflect beta, not ga
leowonderful Feb 5, 2026
5cb710d
Add Observability Project Settings resource
leowonderful Jan 30, 2026
4f4eefc
reflect beta, not ga
leowonderful Feb 5, 2026
ba961ee
make tests less flaky
leowonderful Feb 24, 2026
6560568
fix example tf.tmpl
leowonderful Feb 24, 2026
0f6adb4
override autogen code
leowonderful Feb 24, 2026
834894a
Add beta version guards
leowonderful Feb 25, 2026
b996f5c
Handwritten docs for the datasource
leowonderful Feb 25, 2026
3fdd5f5
run `gofmt` on the templates
leowonderful Feb 25, 2026
7c775da
format yaml correctly
leowonderful Feb 25, 2026
6c3cf88
Add missing version guard
leowonderful Feb 25, 2026
acb6c75
eof newlife for yaml
leowonderful Feb 25, 2026
0549bca
Add REST API reference to handwritten datasource docs
leowonderful Feb 25, 2026
3ee5e00
just use a simple pre_* function
leowonderful Feb 26, 2026
5033c12
simplify datasource
leowonderful Feb 26, 2026
0771428
doc linkback
leowonderful Feb 26, 2026
39fc344
make docs reflect beta
leowonderful Feb 26, 2026
20f89a8
fmt fix
leowonderful Feb 26, 2026
c88c65d
put import block into version guard
leowonderful Feb 26, 2026
de66594
Reduce hardcoding to make datasource more robust
leowonderful Feb 26, 2026
c5f79da
Make datasource tests more durable, and also allow passing empty stri…
leowonderful Feb 26, 2026
9c66aa6
Simplify pre_* and add new guide to datasource documentation
leowonderful Feb 27, 2026
a99c13a
Add back GetRawConfig :|
leowonderful Feb 27, 2026
e4e896d
Move tests, and use `update_mask` to get rid of the pre_update=
leowonderful Feb 27, 2026
5e4e5f4
yaml fmt
leowonderful Feb 27, 2026
d6211ec
Merge branch 'main' into observability-project-settings-resource
leowonderful Mar 2, 2026
11d9daa
Fix "too many arguments" by custom defining Wait operation
leowonderful Mar 2, 2026
ff4dc7c
remove custom operation and fix OrganizationSettings yaml
leowonderful Mar 3, 2026
4873a9e
rerun CI
leowonderful Mar 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mmv1/products/observability/OrganizationSettings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import_format:
- 'organizations/{{organization}}/locations/{{location}}/settings'

async:
include_project: true
operation:
timeouts:
insert_minutes: 10
Expand Down
90 changes: 90 additions & 0 deletions mmv1/products/observability/ProjectSettings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright 2026 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---
name: 'ProjectSettings'
description: Manages Cloud Observability settings for a project.
min_version: 'beta'
base_url: 'projects/{{project}}/locations/{{location}}/settings'
self_link: 'projects/{{project}}/locations/{{location}}/settings'

create_url: 'projects/{{project}}/locations/{{location}}/settings'
create_verb: 'PATCH'
update_url: 'projects/{{project}}/locations/{{location}}/settings'
update_verb: 'PATCH'
import_format:
- "projects/{{project}}/locations/{{location}}/settings"
exclude_delete: true
autogen_async: true
update_mask: true

custom_code:
pre_create: templates/terraform/pre_create/observability_project_settings.go.tmpl

async:
operation:
timeouts:
insert_minutes: 10
update_minutes: 10
base_url: '{{op_id}}'
result:
resource_inside_response: true

examples:
- name: "observability_project_settings_basic"
config_path: "templates/terraform/examples/observability_project_settings_basic.tf.tmpl"
primary_resource_id: "primary"
external_providers: ["time"]
vars:
location: "us"
kms_key_name: "example-key"
test_env_vars:
project_name: 'PROJECT_NAME'
org_id: 'ORG_ID'
billing_account: 'BILLING_ACCOUNT'
test_vars_overrides:
kms_key_name: 'acctest.BootstrapKMSKeyInLocation(t, "us").CryptoKey.Name'
- name: "observability_project_settings_basic_global"
config_path: "templates/terraform/examples/observability_project_settings_basic_global.tf.tmpl"
primary_resource_id: "primary_global"
external_providers: ["time"]
vars:
location: "global"
test_env_vars:
org_id: 'ORG_ID'
project_name: 'PROJECT_NAME'
billing_account: 'BILLING_ACCOUNT'

parameters:
- name: 'location'
type: String
description: 'The location of the settings.'
url_param_only: true
required: true
immutable: true

properties:
- name: 'defaultStorageLocation'
type: String
description: 'The default storage location for new resources, e.g. buckets. Only valid for global location.'
- name: 'kmsKeyName'
type: String
description: 'The default Cloud KMS key to use for new resources. Only valid for regional locations.'
- name: 'name'
type: String
description: 'The resource name of the settings.'
output: true
- name: 'serviceAccountId'
type: String
description: 'The service account used by Cloud Observability for this project.'
output: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
resource "google_project" "project" {
provider = "google-beta"
project_id = "tf-test%{random_suffix}"
name = "tf-test%{random_suffix}"
org_id = "%{org_id}"
deletion_policy = "DELETE"
}

resource "google_project_service" "observability_api" {
provider = "google-beta"
project = google_project.project.project_id
service = "observability.googleapis.com"
disable_on_destroy = false
}

# Actively force the creation of the Service Agent identity
resource "google_project_service_identity" "observability_sa" {
provider = "google-beta"
project = google_project.project.project_id
service = "observability.googleapis.com"

depends_on = [
google_project_service.observability_api
]
}

# Short buffer for the new identity to propagate to global IAM indexes
resource "time_sleep" "wait_for_sa_propagation" {
create_duration = "30s"
depends_on = [
google_project_service_identity.observability_sa
]
}

resource "google_kms_crypto_key_iam_member" "crypto_key" {
provider = "google-beta"
crypto_key_id = "%{kms_key_name}"
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:${google_project_service_identity.observability_sa.email}"

depends_on = [
time_sleep.wait_for_sa_propagation
]
}

resource "google_observability_project_settings" "primary" {
provider = "google-beta"
location = "us"
project = google_project.project.project_id
kms_key_name = "%{kms_key_name}"

depends_on = [
google_kms_crypto_key_iam_member.crypto_key
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
resource "google_project" "project" {
provider = "google-beta"
project_id = "tf-test%{random_suffix}"
name = "tf-test%{random_suffix}"
org_id = "{{index $.TestEnvVars "org_id"}}"
deletion_policy = "DELETE"
}

resource "google_project_service" "observability_api" {
provider = "google-beta"
project = google_project.project.project_id
service = "observability.googleapis.com"
disable_on_destroy = false
}

# Wait for the project to be created and recognized by the Observability API
resource "time_sleep" "wait_for_settings_propagation" {
create_duration = "90s"
depends_on = [google_project_service.observability_api]
}

resource "google_observability_project_settings" "primary_global" {
provider = "google-beta"
location = "global"
project = google_project.project.project_id
default_storage_location = "eu"
depends_on = [time_sleep.wait_for_settings_propagation]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var masks []string
if !d.GetRawConfig().GetAttr("default_storage_location").IsNull() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think d.GetRawConfig() is a little heavy-handed for what you want. It would be easier / more clear to introspect what's already in obj. It would also be nice (if possible) to avoid setting values in obj here since they're already set above. Is there a specific reason they need to be re-set?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this is heavy-handed, however, I tried using d.GetOkExists and just obj["defaultStorageLocation"] instead and it always failed testing on the empty string case in the datasource test (the mask was not added with either of those cases, and the API complained)

d.GetRawConfig() was my last resort to have it reliably detect this case, but I agree we can limit this to mask computation only and stop re-setting obj.

masks = append(masks, "defaultStorageLocation")
}
if !d.GetRawConfig().GetAttr("kms_key_name").IsNull() {
masks = append(masks, "kmsKeyName")
}

if len(masks) > 0 {
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(masks, ",")})
if err != nil {
return err
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ var handwrittenDatasources = map[string]*schema.Resource{
"google_monitoring_uptime_check_ips": monitoring.DataSourceGoogleMonitoringUptimeCheckIps(),
"google_netblock_ip_ranges": resourcemanager.DataSourceGoogleNetblockIpRanges(),
{{- if ne $.TargetVersionName "ga" }}
"google_observability_project_settings": observability.DataSourceObservabilityProjectSettings(),
"google_observability_organization_settings": observability.DataSourceObservabilityOrganizationSettings(),
{{- end }}
"google_oracle_database_autonomous_database": oracledatabase.DataSourceOracleDatabaseAutonomousDatabase(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package observability

{{if ne $.TargetVersionName "ga" -}}
import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
)

func DataSourceObservabilityProjectSettings() *schema.Resource {
Comment thread
melinath marked this conversation as resolved.
dsSchema := tpgresource.DatasourceSchemaFromResourceSchema(ResourceObservabilityProjectSettings().Schema)

tpgresource.AddRequiredFieldsToSchema(dsSchema, "project")
tpgresource.AddRequiredFieldsToSchema(dsSchema, "location")

return &schema.Resource{
Read: dataSourceObservabilityProjectSettingsRead,
Schema: dsSchema,
}
}

func dataSourceObservabilityProjectSettingsRead(d *schema.ResourceData, meta interface{}) error {
Comment thread
leowonderful marked this conversation as resolved.
project := d.Get("project").(string)
location := d.Get("location").(string)

id := fmt.Sprintf("projects/%s/locations/%s/settings", project, location)
d.SetId(id)

err := resourceObservabilityProjectSettingsRead(d, meta)
if err != nil {
return nil
}
if d.Id() == "" {
return fmt.Errorf("%s not found", id)
}
return nil
}
{{- end }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package observability_test

{{ if ne $.TargetVersionName "ga" -}}
import (
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
)

func TestAccObservabilityProjectSettings_datasource(t *testing.T) {
t.Parallel()
context := map[string]interface{}{
"project_name": "tf-test-" + acctest.RandString(t, 10),
"org_id": envvar.GetTestOrgFromEnv(t),
"billing_account": envvar.GetTestBillingAccountFromEnv(t),
"location": "us",
}
dataResourceName := "data.google_observability_project_settings.settings"
acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t),
ExternalProviders: map[string]resource.ExternalProvider{
"time": {},
},
Steps: []resource.TestStep{
{
Config: testAccObservabilityProjectSettings_datasource(context),
Check: acctest.CheckDataSourceStateMatchesResourceState(dataResourceName, "google_observability_project_settings.settings"),
},
},
})
}
func testAccObservabilityProjectSettings_datasource(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_project" "default" {
provider = "google-beta"
project_id = "%{project_name}"
name = "%{project_name}"
org_id = "%{org_id}"
billing_account = "%{billing_account}"
deletion_policy = "DELETE"
}
resource "google_project_service" "observability_service" {
provider = "google-beta"
project = google_project.default.project_id
service = "observability.googleapis.com"
disable_on_destroy = false
}
resource "time_sleep" "wait_for_project" {
create_duration = "60s"
depends_on = [google_project_service.observability_service]
}

resource "google_observability_project_settings" "settings" {
provider = "google-beta"
project = google_project.default.project_id
location = "%{location}"
kms_key_name = ""
depends_on = [time_sleep.wait_for_project]
}

data "google_observability_project_settings" "settings" {
provider = "google-beta"
project = google_project.default.project_id
location = "%{location}"
depends_on = [google_observability_project_settings.settings]
}
`, context)
}
{{- else }}
{{- end }}
Loading
Loading