diff --git a/.changelog/4185.txt b/.changelog/4185.txt new file mode 100644 index 0000000000..dc5625ea6b --- /dev/null +++ b/.changelog/4185.txt @@ -0,0 +1,12 @@ +```release-note:new-resource +google_healthcare_consent_store_iam_binding +``` +```release-note:new-resource +google_healthcare_consent_store_iam_member +``` +```release-note:new-resource +google_healthcare_consent_store_iam_policy +``` +```release-note:new-resource +google_healthcare_consent_store +``` diff --git a/google-beta/iam_healthcare_consent_store.go b/google-beta/iam_healthcare_consent_store.go new file mode 100644 index 0000000000..630745e70e --- /dev/null +++ b/google-beta/iam_healthcare_consent_store.go @@ -0,0 +1,181 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var HealthcareConsentStoreIamSchema = map[string]*schema.Schema{ + "dataset": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "consent_store_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, +} + +type HealthcareConsentStoreIamUpdater struct { + dataset string + consentStoreId string + d *schema.ResourceData + Config *Config +} + +func HealthcareConsentStoreIamUpdaterProducer(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { + values := make(map[string]string) + + if v, ok := d.GetOk("dataset"); ok { + values["dataset"] = v.(string) + } + + if v, ok := d.GetOk("consent_store_id"); ok { + values["consent_store_id"] = v.(string) + } + + // We may have gotten either a long or short name, so attempt to parse long name if possible + m, err := getImportIdQualifiers([]string{"(?P.+)/consentStores/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Get("consent_store_id").(string)) + if err != nil { + return nil, err + } + + for k, v := range m { + values[k] = v + } + + u := &HealthcareConsentStoreIamUpdater{ + dataset: values["dataset"], + consentStoreId: values["consent_store_id"], + d: d, + Config: config, + } + + if err := d.Set("dataset", u.dataset); err != nil { + return nil, fmt.Errorf("Error setting dataset: %s", err) + } + if err := d.Set("consent_store_id", u.GetResourceId()); err != nil { + return nil, fmt.Errorf("Error setting consent_store_id: %s", err) + } + + return u, nil +} + +func HealthcareConsentStoreIdParseFunc(d *schema.ResourceData, config *Config) error { + values := make(map[string]string) + + m, err := getImportIdQualifiers([]string{"(?P.+)/consentStores/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Id()) + if err != nil { + return err + } + + for k, v := range m { + values[k] = v + } + + u := &HealthcareConsentStoreIamUpdater{ + dataset: values["dataset"], + consentStoreId: values["consent_store_id"], + d: d, + Config: config, + } + if err := d.Set("consent_store_id", u.GetResourceId()); err != nil { + return fmt.Errorf("Error setting consent_store_id: %s", err) + } + d.SetId(u.GetResourceId()) + return nil +} + +func (u *HealthcareConsentStoreIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + url, err := u.qualifyConsentStoreUrl("getIamPolicy") + if err != nil { + return nil, err + } + + var obj map[string]interface{} + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return nil, err + } + + policy, err := sendRequest(u.Config, "GET", "", url, userAgent, obj) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + out := &cloudresourcemanager.Policy{} + err = Convert(policy, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err) + } + + return out, nil +} + +func (u *HealthcareConsentStoreIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + json, err := ConvertToMap(policy) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + obj["policy"] = json + + url, err := u.qualifyConsentStoreUrl("setIamPolicy") + if err != nil { + return err + } + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return err + } + + _, err = sendRequestWithTimeout(u.Config, "POST", "", url, userAgent, obj, u.d.Timeout(schema.TimeoutCreate)) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *HealthcareConsentStoreIamUpdater) qualifyConsentStoreUrl(methodIdentifier string) (string, error) { + urlTemplate := fmt.Sprintf("{{HealthcareBasePath}}%s:%s", fmt.Sprintf("%s/consentStores/%s", u.dataset, u.consentStoreId), methodIdentifier) + url, err := replaceVars(u.d, u.Config, urlTemplate) + if err != nil { + return "", err + } + return url, nil +} + +func (u *HealthcareConsentStoreIamUpdater) GetResourceId() string { + return fmt.Sprintf("%s/consentStores/%s", u.dataset, u.consentStoreId) +} + +func (u *HealthcareConsentStoreIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-healthcare-consentstore-%s", u.GetResourceId()) +} + +func (u *HealthcareConsentStoreIamUpdater) DescribeResource() string { + return fmt.Sprintf("healthcare consentstore %q", u.GetResourceId()) +} diff --git a/google-beta/iam_healthcare_consent_store_generated_test.go b/google-beta/iam_healthcare_consent_store_generated_test.go new file mode 100644 index 0000000000..59de60e6a7 --- /dev/null +++ b/google-beta/iam_healthcare_consent_store_generated_test.go @@ -0,0 +1,227 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccHealthcareConsentStoreIamBindingGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + Steps: []resource.TestStep{ + { + Config: testAccHealthcareConsentStoreIamBinding_basicGenerated(context), + }, + { + // Test Iam Binding update + Config: testAccHealthcareConsentStoreIamBinding_updateGenerated(context), + }, + }, + }) +} + +func TestAccHealthcareConsentStoreIamMemberGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccHealthcareConsentStoreIamMember_basicGenerated(context), + }, + }, + }) +} + +func TestAccHealthcareConsentStoreIamPolicyGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + Steps: []resource.TestStep{ + { + Config: testAccHealthcareConsentStoreIamPolicy_basicGenerated(context), + }, + { + Config: testAccHealthcareConsentStoreIamPolicy_emptyBinding(context), + }, + }, + }) +} + +func testAccHealthcareConsentStoreIamMember_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" +} + +resource "google_healthcare_consent_store_iam_member" "foo" { + provider = google-beta + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + role = "%{role}" + member = "user:admin@hashicorptest.com" +} +`, context) +} + +func testAccHealthcareConsentStoreIamPolicy_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" +} + +data "google_iam_policy" "foo" { + provider = google-beta + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + } +} + +resource "google_healthcare_consent_store_iam_policy" "foo" { + provider = google-beta + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccHealthcareConsentStoreIamPolicy_emptyBinding(context map[string]interface{}) string { + return Nprintf(` +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" +} + +data "google_iam_policy" "foo" { + provider = google-beta +} + +resource "google_healthcare_consent_store_iam_policy" "foo" { + provider = google-beta + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccHealthcareConsentStoreIamBinding_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" +} + +resource "google_healthcare_consent_store_iam_binding" "foo" { + + provider = google-beta + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} +`, context) +} + +func testAccHealthcareConsentStoreIamBinding_updateGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" +} + +resource "google_healthcare_consent_store_iam_binding" "foo" { + provider = google-beta + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + role = "%{role}" + members = ["user:admin@hashicorptest.com", "user:paddy@hashicorp.com"] +} +`, context) +} diff --git a/google-beta/provider.go b/google-beta/provider.go index 0356c3264c..793d5bffac 100644 --- a/google-beta/provider.go +++ b/google-beta/provider.go @@ -767,9 +767,9 @@ func Provider() *schema.Provider { return provider } -// Generated resources: 200 -// Generated IAM resources: 102 -// Total generated resources: 302 +// Generated resources: 201 +// Generated IAM resources: 105 +// Total generated resources: 306 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -974,6 +974,10 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_healthcare_dicom_store": resourceHealthcareDicomStore(), "google_healthcare_fhir_store": resourceHealthcareFhirStore(), "google_healthcare_hl7_v2_store": resourceHealthcareHl7V2Store(), + "google_healthcare_consent_store": resourceHealthcareConsentStore(), + "google_healthcare_consent_store_iam_binding": ResourceIamBinding(HealthcareConsentStoreIamSchema, HealthcareConsentStoreIamUpdaterProducer, HealthcareConsentStoreIdParseFunc), + "google_healthcare_consent_store_iam_member": ResourceIamMember(HealthcareConsentStoreIamSchema, HealthcareConsentStoreIamUpdaterProducer, HealthcareConsentStoreIdParseFunc), + "google_healthcare_consent_store_iam_policy": ResourceIamPolicy(HealthcareConsentStoreIamSchema, HealthcareConsentStoreIamUpdaterProducer, HealthcareConsentStoreIdParseFunc), "google_iam_workload_identity_pool": resourceIAMBetaWorkloadIdentityPool(), "google_iam_workload_identity_pool_provider": resourceIAMBetaWorkloadIdentityPoolProvider(), "google_iap_web_iam_binding": ResourceIamBinding(IapWebIamSchema, IapWebIamUpdaterProducer, IapWebIdParseFunc), diff --git a/google-beta/resource_healthcare_consent_store.go b/google-beta/resource_healthcare_consent_store.go new file mode 100644 index 0000000000..f9bc1a3a60 --- /dev/null +++ b/google-beta/resource_healthcare_consent_store.go @@ -0,0 +1,338 @@ +// ---------------------------------------------------------------------------- +// +// *** 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-plugin-sdk/v2/helper/schema" +) + +func resourceHealthcareConsentStore() *schema.Resource { + return &schema.Resource{ + Create: resourceHealthcareConsentStoreCreate, + Read: resourceHealthcareConsentStoreRead, + Update: resourceHealthcareConsentStoreUpdate, + Delete: resourceHealthcareConsentStoreDelete, + + Importer: &schema.ResourceImporter{ + State: resourceHealthcareConsentStoreImport, + }, + + 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{ + "dataset": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `Identifies the dataset addressed by this request. Must be in the format +'projects/{project}/locations/{location}/datasets/{dataset}'`, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The name of this ConsentStore, for example: +"consent1"`, + }, + "default_consent_ttl": { + Type: schema.TypeString, + Optional: true, + Description: `Default time to live for consents in this store. Must be at least 24 hours. Updating this field will not affect the expiration time of existing consents. + +A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s".`, + }, + "enable_consent_create_on_update": { + Type: schema.TypeBool, + Optional: true, + Description: `If true, [consents.patch] [google.cloud.healthcare.v1beta1.consent.UpdateConsent] creates the consent if it does not already exist.`, + }, + "labels": { + Type: schema.TypeMap, + Optional: true, + Description: `User-supplied key-value pairs used to organize Consent stores. + +Label keys must be between 1 and 63 characters long, have a UTF-8 encoding of maximum 128 bytes, and must +conform to the following PCRE regular expression: '[\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}' + +Label values are optional, must be between 1 and 63 characters long, have a UTF-8 encoding of maximum 128 +bytes, and must conform to the following PCRE regular expression: '[\p{Ll}\p{Lo}\p{N}_-]{0,63}' + +No more than 64 labels can be associated with a given store. + +An object containing a list of "key": value pairs. +Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }.`, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func resourceHealthcareConsentStoreCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + defaultConsentTtlProp, err := expandHealthcareConsentStoreDefaultConsentTtl(d.Get("default_consent_ttl"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("default_consent_ttl"); !isEmptyValue(reflect.ValueOf(defaultConsentTtlProp)) && (ok || !reflect.DeepEqual(v, defaultConsentTtlProp)) { + obj["defaultConsentTtl"] = defaultConsentTtlProp + } + enableConsentCreateOnUpdateProp, err := expandHealthcareConsentStoreEnableConsentCreateOnUpdate(d.Get("enable_consent_create_on_update"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("enable_consent_create_on_update"); !isEmptyValue(reflect.ValueOf(enableConsentCreateOnUpdateProp)) && (ok || !reflect.DeepEqual(v, enableConsentCreateOnUpdateProp)) { + obj["enableConsentCreateOnUpdate"] = enableConsentCreateOnUpdateProp + } + labelsProp, err := expandHealthcareConsentStoreLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + url, err := replaceVars(d, config, "{{HealthcareBasePath}}{{dataset}}/consentStores?consentStoreId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new ConsentStore: %#v", obj) + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating ConsentStore: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "{{dataset}}/consentStores/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating ConsentStore %q: %#v", d.Id(), res) + + return resourceHealthcareConsentStoreRead(d, meta) +} + +func resourceHealthcareConsentStoreRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{HealthcareBasePath}}{{dataset}}/consentStores/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("HealthcareConsentStore %q", d.Id())) + } + + if err := d.Set("default_consent_ttl", flattenHealthcareConsentStoreDefaultConsentTtl(res["defaultConsentTtl"], d, config)); err != nil { + return fmt.Errorf("Error reading ConsentStore: %s", err) + } + if err := d.Set("enable_consent_create_on_update", flattenHealthcareConsentStoreEnableConsentCreateOnUpdate(res["enableConsentCreateOnUpdate"], d, config)); err != nil { + return fmt.Errorf("Error reading ConsentStore: %s", err) + } + if err := d.Set("labels", flattenHealthcareConsentStoreLabels(res["labels"], d, config)); err != nil { + return fmt.Errorf("Error reading ConsentStore: %s", err) + } + + return nil +} + +func resourceHealthcareConsentStoreUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + obj := make(map[string]interface{}) + defaultConsentTtlProp, err := expandHealthcareConsentStoreDefaultConsentTtl(d.Get("default_consent_ttl"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("default_consent_ttl"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, defaultConsentTtlProp)) { + obj["defaultConsentTtl"] = defaultConsentTtlProp + } + enableConsentCreateOnUpdateProp, err := expandHealthcareConsentStoreEnableConsentCreateOnUpdate(d.Get("enable_consent_create_on_update"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("enable_consent_create_on_update"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, enableConsentCreateOnUpdateProp)) { + obj["enableConsentCreateOnUpdate"] = enableConsentCreateOnUpdateProp + } + labelsProp, err := expandHealthcareConsentStoreLabels(d.Get("labels"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { + obj["labels"] = labelsProp + } + + url, err := replaceVars(d, config, "{{HealthcareBasePath}}{{dataset}}/consentStores/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating ConsentStore %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("default_consent_ttl") { + updateMask = append(updateMask, "defaultConsentTtl") + } + + if d.HasChange("enable_consent_create_on_update") { + updateMask = append(updateMask, "enableConsentCreateOnUpdate") + } + + if d.HasChange("labels") { + updateMask = append(updateMask, "labels") + } + // 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 == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "PATCH", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating ConsentStore %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating ConsentStore %q: %#v", d.Id(), res) + } + + return resourceHealthcareConsentStoreRead(d, meta) +} + +func resourceHealthcareConsentStoreDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + + url, err := replaceVars(d, config, "{{HealthcareBasePath}}{{dataset}}/consentStores/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting ConsentStore %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := sendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "ConsentStore") + } + + log.Printf("[DEBUG] Finished deleting ConsentStore %q: %#v", d.Id(), res) + return nil +} + +func resourceHealthcareConsentStoreImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "(?P[^/]+)/consentStores/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "{{dataset}}/consentStores/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenHealthcareConsentStoreDefaultConsentTtl(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenHealthcareConsentStoreEnableConsentCreateOnUpdate(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenHealthcareConsentStoreLabels(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandHealthcareConsentStoreDefaultConsentTtl(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandHealthcareConsentStoreEnableConsentCreateOnUpdate(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandHealthcareConsentStoreLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) { + if v == nil { + return map[string]string{}, nil + } + m := make(map[string]string) + for k, val := range v.(map[string]interface{}) { + m[k] = val.(string) + } + return m, nil +} diff --git a/google-beta/resource_healthcare_consent_store_generated_test.go b/google-beta/resource_healthcare_consent_store_generated_test.go new file mode 100644 index 0000000000..311d3236c9 --- /dev/null +++ b/google-beta/resource_healthcare_consent_store_generated_test.go @@ -0,0 +1,195 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccHealthcareConsentStore_healthcareConsentStoreBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + }, + CheckDestroy: testAccCheckHealthcareConsentStoreDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccHealthcareConsentStore_healthcareConsentStoreBasicExample(context), + }, + }, + }) +} + +func testAccHealthcareConsentStore_healthcareConsentStoreBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" +} +`, context) +} + +func TestAccHealthcareConsentStore_healthcareConsentStoreFullExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + }, + CheckDestroy: testAccCheckHealthcareConsentStoreDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccHealthcareConsentStore_healthcareConsentStoreFullExample(context), + }, + }, + }) +} + +func testAccHealthcareConsentStore_healthcareConsentStoreFullExample(context map[string]interface{}) string { + return Nprintf(` + +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" + + enable_consent_create_on_update = true + default_consent_ttl = "90000s" + + labels = { + "label1" = "labelvalue1" + } +} +`, context) +} + +func TestAccHealthcareConsentStore_healthcareConsentStoreIamExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersOiCS, + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + }, + CheckDestroy: testAccCheckHealthcareConsentStoreDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccHealthcareConsentStore_healthcareConsentStoreIamExample(context), + }, + }, + }) +} + +func testAccHealthcareConsentStore_healthcareConsentStoreIamExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "tf-test-my-dataset%{random_suffix}" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "tf-test-my-consent-store%{random_suffix}" +} + +resource "google_service_account" "test-account" { + provider = google-beta + + account_id = "tf-test-my-account%{random_suffix}" + display_name = "Test Service Account" +} + +resource "google_healthcare_consent_store_iam_member" "test-iam" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + consent_store_id = google_healthcare_consent_store.my-consent.name + role = "roles/editor" + member = "serviceAccount:${google_service_account.test-account.email}" +} +`, context) +} + +func testAccCheckHealthcareConsentStoreDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_healthcare_consent_store" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{HealthcareBasePath}}{{dataset}}/consentStores/{{name}}") + if err != nil { + return err + } + + _, err = sendRequest(config, "GET", "", url, config.userAgent, nil) + if err == nil { + return fmt.Errorf("HealthcareConsentStore still exists at %s", url) + } + } + + return nil + } +} diff --git a/google-beta/resource_healthcare_consent_store_sweeper_test.go b/google-beta/resource_healthcare_consent_store_sweeper_test.go new file mode 100644 index 0000000000..dc95ad1a19 --- /dev/null +++ b/google-beta/resource_healthcare_consent_store_sweeper_test.go @@ -0,0 +1,124 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("HealthcareConsentStore", &resource.Sweeper{ + Name: "HealthcareConsentStore", + F: testSweepHealthcareConsentStore, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepHealthcareConsentStore(region string) error { + resourceName := "HealthcareConsentStore" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://healthcare.googleapis.com/v1beta1/{{dataset}}/consentStores?consentStoreId={{name}}", "?")[0] + listUrl, err := replaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := sendRequest(config, "GET", config.Project, listUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["consentStores"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + return nil + } + + name := GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !isSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://healthcare.googleapis.com/v1beta1/{{dataset}}/consentStores/{{name}}" + deleteUrl, err := replaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = sendRequest(config, "DELETE", config.Project, deleteUrl, config.userAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/website/docs/r/healthcare_consent_store.html.markdown b/website/docs/r/healthcare_consent_store.html.markdown new file mode 100644 index 0000000000..b8968558ba --- /dev/null +++ b/website/docs/r/healthcare_consent_store.html.markdown @@ -0,0 +1,195 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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. +# +# ---------------------------------------------------------------------------- +subcategory: "Cloud Healthcare" +layout: "google" +page_title: "Google: google_healthcare_consent_store" +sidebar_current: "docs-google-healthcare-consent-store" +description: |- + The Consent Management API is a tool for tracking user consents and the documentation associated with the consents. +--- + +# google\_healthcare\_consent\_store + +The Consent Management API is a tool for tracking user consents and the documentation associated with the consents. + +~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. +See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources. + +To get more information about ConsentStore, see: + +* [API documentation](https://cloud.google.com/healthcare/docs/reference/rest/v1beta1/projects.locations.datasets.consentStores) +* How-to Guides + * [Creating a Consent store](https://cloud.google.com/healthcare/docs/how-tos/consent) + + +## Example Usage - Healthcare Consent Store Basic + + +```hcl +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "my-dataset" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "my-consent-store" +} +``` + +## Example Usage - Healthcare Consent Store Full + + +```hcl + +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "my-dataset" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "my-consent-store" + + enable_consent_create_on_update = true + default_consent_ttl = "90000s" + + labels = { + "label1" = "labelvalue1" + } +} +``` + +## Example Usage - Healthcare Consent Store Iam + + +```hcl +resource "google_healthcare_dataset" "dataset" { + provider = google-beta + + location = "us-central1" + name = "my-dataset" +} + +resource "google_healthcare_consent_store" "my-consent" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + name = "my-consent-store" +} + +resource "google_service_account" "test-account" { + provider = google-beta + + account_id = "my-account" + display_name = "Test Service Account" +} + +resource "google_healthcare_consent_store_iam_member" "test-iam" { + provider = google-beta + + dataset = google_healthcare_dataset.dataset.id + consent_store_id = google_healthcare_consent_store.my-consent.name + role = "roles/editor" + member = "serviceAccount:${google_service_account.test-account.email}" +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - + (Required) + The name of this ConsentStore, for example: + "consent1" + +* `dataset` - + (Required) + Identifies the dataset addressed by this request. Must be in the format + 'projects/{project}/locations/{location}/datasets/{dataset}' + + +- - - + + +* `default_consent_ttl` - + (Optional) + Default time to live for consents in this store. Must be at least 24 hours. Updating this field will not affect the expiration time of existing consents. + A duration in seconds with up to nine fractional digits, terminated by 's'. Example: "3.5s". + +* `enable_consent_create_on_update` - + (Optional) + If true, [consents.patch] [google.cloud.healthcare.v1beta1.consent.UpdateConsent] creates the consent if it does not already exist. + +* `labels` - + (Optional) + User-supplied key-value pairs used to organize Consent stores. + Label keys must be between 1 and 63 characters long, have a UTF-8 encoding of maximum 128 bytes, and must + conform to the following PCRE regular expression: `[\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}` + Label values are optional, must be between 1 and 63 characters long, have a UTF-8 encoding of maximum 128 + bytes, and must conform to the following PCRE regular expression: `[\p{Ll}\p{Lo}\p{N}_-]{0,63}` + No more than 64 labels can be associated with a given store. + An object containing a list of "key": value pairs. + Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `{{dataset}}/consentStores/{{name}}` + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 4 minutes. +- `update` - Default is 4 minutes. +- `delete` - Default is 4 minutes. + +## Import + + +ConsentStore can be imported using any of these accepted formats: + +``` +$ terraform import google_healthcare_consent_store.default {{dataset}}/consentStores/{{name}} +$ terraform import google_healthcare_consent_store.default {{dataset}}/{{name}} +$ terraform import google_healthcare_consent_store.default {{name}} +``` diff --git a/website/docs/r/healthcare_consent_store_iam.html.markdown b/website/docs/r/healthcare_consent_store_iam.html.markdown new file mode 100644 index 0000000000..f28d356e8c --- /dev/null +++ b/website/docs/r/healthcare_consent_store_iam.html.markdown @@ -0,0 +1,140 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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. +# +# ---------------------------------------------------------------------------- +subcategory: "Cloud Healthcare" +layout: "google" +page_title: "Google: google_healthcare_consent_store_iam" +sidebar_current: "docs-google-healthcare-consent-store-iam" +description: |- + Collection of resources to manage IAM policy for Cloud Healthcare ConsentStore +--- + +# IAM policy for Cloud Healthcare ConsentStore +Three different resources help you manage your IAM policy for Cloud Healthcare ConsentStore. Each of these resources serves a different use case: + +* `google_healthcare_consent_store_iam_policy`: Authoritative. Sets the IAM policy for the consentstore and replaces any existing policy already attached. +* `google_healthcare_consent_store_iam_binding`: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the consentstore are preserved. +* `google_healthcare_consent_store_iam_member`: Non-authoritative. Updates the IAM policy to grant a role to a new member. Other members for the role for the consentstore are preserved. + +~> **Note:** `google_healthcare_consent_store_iam_policy` **cannot** be used in conjunction with `google_healthcare_consent_store_iam_binding` and `google_healthcare_consent_store_iam_member` or they will fight over what your policy should be. + +~> **Note:** `google_healthcare_consent_store_iam_binding` resources **can be** used in conjunction with `google_healthcare_consent_store_iam_member` resources **only if** they do not grant privilege to the same role. + +~> **Warning:** This resource is in beta, and should be used with the terraform-provider-google-beta provider. +See [Provider Versions](https://terraform.io/docs/providers/google/guides/provider_versions.html) for more details on beta resources. + + +## google\_healthcare\_consent\_store\_iam\_policy + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/viewer" + members = [ + "user:jane@example.com", + ] + } +} + +resource "google_healthcare_consent_store_iam_policy" "policy" { + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + policy_data = data.google_iam_policy.admin.policy_data +} +``` + +## google\_healthcare\_consent\_store\_iam\_binding + +```hcl +resource "google_healthcare_consent_store_iam_binding" "binding" { + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + role = "roles/viewer" + members = [ + "user:jane@example.com", + ] +} +``` + +## google\_healthcare\_consent\_store\_iam\_member + +```hcl +resource "google_healthcare_consent_store_iam_member" "member" { + dataset = google_healthcare_consent_store.my-consent.dataset + consent_store_id = google_healthcare_consent_store.my-consent.name + role = "roles/viewer" + member = "user:jane@example.com" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `consent_store_id` - (Required) Used to find the parent resource to bind the IAM policy to +* `dataset` - (Required) Identifies the dataset addressed by this request. Must be in the format +'projects/{project}/locations/{location}/datasets/{dataset}' + Used to find the parent resource to bind the IAM policy to + +* `member/members` - (Required) Identities that will be granted the privilege in `role`. + Each entry can have one of the following values: + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. + * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + +* `role` - (Required) The role that should be applied. Only one + `google_healthcare_consent_store_iam_binding` can be used per role. Note that custom roles must be of the format + `[projects|organizations]/{parent-name}/roles/{role-name}`. + +* `policy_data` - (Required only by `google_healthcare_consent_store_iam_policy`) The policy data generated by + a `google_iam_policy` data source. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `etag` - (Computed) The etag of the IAM policy. + +## Import + +For all import syntaxes, the "resource in question" can take any of the following forms: + +* {{dataset}}/consentStores/{{name}} +* {{name}} + +Any variables not passed in the import command will be taken from the provider configuration. + +Cloud Healthcare consentstore IAM resources can be imported using the resource identifiers, role, and member. + +IAM member imports use space-delimited identifiers: the resource in question, the role, and the member identity, e.g. +``` +$ terraform import google_healthcare_consent_store_iam_member.editor "{{dataset}}/consentStores/{{consent_store}} roles/viewer user:jane@example.com" +``` + +IAM binding imports use space-delimited identifiers: the resource in question and the role, e.g. +``` +$ terraform import google_healthcare_consent_store_iam_binding.editor "{{dataset}}/consentStores/{{consent_store}} roles/viewer" +``` + +IAM policy imports use the identifier of the resource in question, e.g. +``` +$ terraform import google_healthcare_consent_store_iam_policy.editor {{dataset}}/consentStores/{{consent_store}} +``` + +-> **Custom Roles**: If you're importing a IAM resource with a custom role, make sure to use the + full name of the custom role, e.g. `[projects/my-project|organizations/my-org]/roles/my-custom-role`. diff --git a/website/google.erb b/website/google.erb index df505028f1..fec0035956 100644 --- a/website/google.erb +++ b/website/google.erb @@ -748,6 +748,14 @@ Resources