From eb8b4d88e7b67cd854e93bf0eee11a9ff831965a Mon Sep 17 00:00:00 2001 From: Stephen Lewis Date: Thu, 3 Sep 2026 12:06:21 -0700 Subject: [PATCH 1/2] Added dlp ContentPolicy resource --- mmv1/products/dlp/ContentPolicy.yaml | 805 ++++++++++++++++++ .../dlp_content_policy_import.go.tmpl | 44 + .../dlp/dlp_content_policy_basic.tf.tmpl | 29 + .../dlp/dlp_content_policy_full.tf.tmpl | 304 +++++++ .../dlp_content_policy_full_update.tf.tmpl | 306 +++++++ 5 files changed, 1488 insertions(+) create mode 100644 mmv1/products/dlp/ContentPolicy.yaml create mode 100644 mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl create mode 100644 mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_basic.tf.tmpl create mode 100644 mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_full.tf.tmpl create mode 100644 mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_full_update.tf.tmpl diff --git a/mmv1/products/dlp/ContentPolicy.yaml b/mmv1/products/dlp/ContentPolicy.yaml new file mode 100644 index 000000000000..510cf2120afb --- /dev/null +++ b/mmv1/products/dlp/ContentPolicy.yaml @@ -0,0 +1,805 @@ +# 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: ContentPolicy +description: | + A policy to apply to content based on its inspection findings. +references: + guides: + Official Documentation: https://cloud.google.com/sensitive-data-protection/docs + api: https://cloud.google.com/sensitive-data-protection/docs/reference/rest/v2/projects.locations.contentPolicies +base_url: '{{parent}}/contentPolicies' +self_link: '{{parent}}/contentPolicies/{{name}}' +create_url: '{{parent}}/contentPolicies' +delete_url: '{{parent}}/contentPolicies/{{name}}' +update_mask: true +update_verb: PATCH +id_format: '{{parent}}/contentPolicies/{{name}}' +timeouts: + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +exclude_sweeper: true +custom_code: + encoder: templates/terraform/encoders/wrap_object.go.tmpl + update_encoder: templates/terraform/encoders/wrap_object.go.tmpl + decoder: templates/terraform/decoders/unwrap_resource.go.tmpl + custom_import: templates/terraform/custom_import/dlp_content_policy_import.go.tmpl +samples: + - name: dlp_content_policy_basic + primary_resource_id: basic + steps: + - name: dlp_content_policy_basic + test_env_vars: + project: PROJECT_NAME + location: REGION + - name: dlp_content_policy_full + primary_resource_id: full + steps: + - name: dlp_content_policy_full + resource_id_vars: + dataset_id: dlp_ds + table_id: dlp_tbl + bucket_name: dlp_bkt + test_env_vars: + project: PROJECT_NAME + location: REGION + - name: dlp_content_policy_full_update + resource_id_vars: + dataset_id: dlp_ds + table_id: dlp_tbl + bucket_name: dlp_bkt + test_env_vars: + project: PROJECT_NAME + location: REGION +parameters: + - name: parent + type: String + description: | + The parent of the content policy in any of the following formats: + + * `projects/{{project}}/locations/{{location}}` + url_param_only: true + required: true + immutable: true +properties: + - name: name + type: String + description: | + The resource name of the content policy. Set by the server. + output: true + custom_flatten: templates/terraform/custom_flatten/name_from_self_link.tmpl + - name: displayName + type: String + description: | + Display name (max 63 chars). + - name: inspectConfig + type: NestedObject + description: InspectConfig to use to produce findings. + properties: + - name: excludeInfoTypes + type: Boolean + description: When true, excludes type information of the findings. + - name: includeQuote + type: Boolean + description: When true, a contextual quote from the data that triggered a finding is included in the response. + - name: minLikelihood + type: Enum + description: | + Only returns findings equal or above this threshold. See https://cloud.google.com/dlp/docs/likelihood for more info + default_value: POSSIBLE + enum_values: + - VERY_UNLIKELY + - UNLIKELY + - POSSIBLE + - LIKELY + - VERY_LIKELY + - name: minLikelihoodPerInfoType + type: Array + description: | + Minimum likelihood per infotype. For each infotype, a user can specify a minimum likelihood. + The system only returns a finding if its likelihood is above this threshold. If this field + is not set, the system uses the InspectConfig min_likelihood. + item_type: + type: NestedObject + properties: + - name: infoType + type: NestedObject + description: | + Type of information the likeliness threshold applies to. Only one likelihood per info_type should be provided. + If InfoTypeLikelihood does not have an info_type, the configuration fails. + properties: + - name: name + type: String + description: | + Name of the information type. Either a name of your choosing when creating a CustomInfoType, or one of the names listed + at https://cloud.google.com/dlp/docs/infotypes-reference when specifying a built-in type. + required: true + - name: version + type: String + description: | + Version name for this InfoType. + - name: minLikelihood + type: Enum + description: | + Only returns findings equal or above this threshold. See https://cloud.google.com/dlp/docs/likelihood for more info. + required: true + enum_values: + - VERY_UNLIKELY + - UNLIKELY + - POSSIBLE + - LIKELY + - VERY_LIKELY + - name: limits + type: NestedObject + description: Configuration to control the number of findings returned. + properties: + - name: maxFindingsPerItem + type: Integer + description: Max number of findings that will be returned for each item scanned. The maximum returned is 2000. + required: true + - name: maxFindingsPerRequest + type: Integer + description: Max number of findings that will be returned per request/job. The maximum returned is 2000. + required: true + - name: maxFindingsPerInfoType + type: Array + description: | + Configuration of findings limit given for specified infoTypes. + item_type: + type: NestedObject + properties: + - name: infoType + type: NestedObject + description: | + Type of information the findings limit applies to. Only one limit per infoType should be provided. If InfoTypeLimit does + not have an infoType, the DLP API applies the limit against all infoTypes that are found but not + specified in another InfoTypeLimit. + properties: + - name: name + type: String + description: | + Name of the information type. Either a name of your choosing when creating a CustomInfoType, or one of the names listed + at https://cloud.google.com/dlp/docs/infotypes-reference when specifying a built-in type. + required: true + - name: version + type: String + description: | + Version name for this InfoType. + - name: sensitivityScore + type: NestedObject + description: | + Optional custom sensitivity for this InfoType. This only applies to data profiling. + properties: + - name: score + type: Enum + description: | + The sensitivity score applied to the resource. + required: true + enum_values: + - SENSITIVITY_LOW + - SENSITIVITY_MODERATE + - SENSITIVITY_HIGH + - name: maxFindings + type: Integer + description: Max findings limit for the given infoType. + required: true + - name: infoTypes + type: Array + description: | + Restricts what infoTypes to look for. The values must correspond to InfoType values returned by infoTypes.list + or listed at https://cloud.google.com/dlp/docs/infotypes-reference. + + When no InfoTypes or CustomInfoTypes are specified in a request, the system may automatically choose what detectors to run. + By default this may be all types, but may change over time as detectors are updated. + item_type: + type: NestedObject + properties: + - name: name + type: String + description: | + Name of the information type. Either a name of your choosing when creating a CustomInfoType, or one of the names listed + at https://cloud.google.com/dlp/docs/infotypes-reference when specifying a built-in type. + required: true + - name: version + type: String + description: | + Version of the information type to use. By default, the version is set to stable + - name: sensitivityScore + type: NestedObject + description: | + Optional custom sensitivity for this InfoType. This only applies to data profiling. + properties: + - name: score + type: Enum + description: | + The sensitivity score applied to the resource. + required: true + enum_values: + - SENSITIVITY_LOW + - SENSITIVITY_MODERATE + - SENSITIVITY_HIGH + - name: contentOptions + type: Array + description: | + List of options defining data content to scan. If empty, text, images, and other content will be included. + item_type: + type: Enum + description: | + This field only has a name and description because of MM + limitations. It should not appear in downstreams. + enum_values: + - CONTENT_TEXT + - CONTENT_IMAGE + - name: ruleSet + type: Array + description: | + Set of rules to apply to the findings for this InspectConfig. Exclusion rules, contained in the set are executed in the end, + other rules are executed in the order they are specified for each info type. + item_type: + type: NestedObject + properties: + - name: infoTypes + type: Array + description: | + List of infoTypes this rule set is applied to. + required: true + item_type: + type: NestedObject + properties: + - name: name + type: String + description: | + Name of the information type. Either a name of your choosing when creating a CustomInfoType, or one of the names listed + at https://cloud.google.com/dlp/docs/infotypes-reference when specifying a built-in type. + required: true + - name: version + type: String + description: | + Version name for this InfoType. + - name: sensitivityScore + type: NestedObject + description: | + Optional custom sensitivity for this InfoType. This only applies to data profiling. + properties: + - name: score + type: Enum + description: | + The sensitivity score applied to the resource. + required: true + enum_values: + - SENSITIVITY_LOW + - SENSITIVITY_MODERATE + - SENSITIVITY_HIGH + - name: rules + type: Array + description: | + Set of rules to be applied to infoTypes. The rules are applied in order. + required: true + item_type: + type: NestedObject + properties: + - name: hotwordRule + type: NestedObject + description: Hotword-based detection rule. + properties: + - name: hotwordRegex + type: NestedObject + description: Regular expression pattern defining what qualifies as a hotword. + required: true + properties: + - name: pattern + type: String + description: | + Pattern defining the regular expression. Its syntax + (https://github.com/google/re2/wiki/Syntax) can be found under the google/re2 repository on GitHub. + required: true + - name: groupIndexes + type: Array + description: | + The index of the submatch to extract as findings. When not specified, + the entire match is returned. No more than 3 may be included. + item_type: + type: Integer + - name: proximity + type: NestedObject + description: | + Proximity of the finding within which the entire hotword must reside. The total length of the window cannot + exceed 1000 characters. Note that the finding itself will be included in the window, so that hotwords may be + used to match substrings of the finding itself. For example, the certainty of a phone number regex + `(\d{3}) \d{3}-\d{4}` could be adjusted upwards if the area code is known to be the local area code of a company + office using the hotword regex `(xxx)`, where `xxx` is the area code in question. + required: true + properties: + - name: windowBefore + type: Integer + description: | + Number of characters before the finding to consider. Either this or window_after must be specified + - name: windowAfter + type: Integer + description: | + Number of characters after the finding to consider. Either this or window_before must be specified + - name: likelihoodAdjustment + type: NestedObject + description: | + Likelihood adjustment to apply to all matching findings. + required: true + properties: + - name: fixedLikelihood + type: Enum + description: | + Set the likelihood of a finding to a fixed value. Either this or relative_likelihood can be set. + enum_values: + - VERY_UNLIKELY + - UNLIKELY + - POSSIBLE + - LIKELY + - VERY_LIKELY + - name: relativeLikelihood + type: Integer + description: | + Increase or decrease the likelihood by the specified number of levels. For example, + if a finding would be POSSIBLE without the detection rule and relativeLikelihood is 1, + then it is upgraded to LIKELY, while a value of -1 would downgrade it to UNLIKELY. + Likelihood may never drop below VERY_UNLIKELY or exceed VERY_LIKELY, so applying an + adjustment of 1 followed by an adjustment of -1 when base likelihood is VERY_LIKELY + will result in a final likelihood of LIKELY. Either this or fixed_likelihood can be set. + - name: exclusionRule + type: NestedObject + description: The rule that specifies conditions when findings of infoTypes specified in InspectionRuleSet are removed from results. + properties: + - name: matchingType + type: Enum + description: | + How the rule is applied. See the documentation for more information: https://cloud.google.com/dlp/docs/reference/rest/v2/InspectConfig#MatchingType + required: true + enum_values: + - MATCHING_TYPE_FULL_MATCH + - MATCHING_TYPE_PARTIAL_MATCH + - MATCHING_TYPE_INVERSE_MATCH + - name: dictionary + type: NestedObject + description: Dictionary which defines the rule. + properties: + - name: wordList + type: NestedObject + description: List of words or phrases to search for. + properties: + - name: words + type: Array + description: | + Words or phrases defining the dictionary. The dictionary must contain at least one + phrase and every phrase must contain at least 2 characters that are letters or digits. + required: true + item_type: + type: String + - name: cloudStoragePath + type: NestedObject + description: Newline-delimited file of words in Cloud Storage. Only a single file is accepted. + properties: + - name: path + type: String + description: | + A url representing a file or path (no wildcards) in Cloud Storage. Example: `gs://[BUCKET_NAME]/dictionary.txt` + required: true + - name: regex + type: NestedObject + description: Regular expression which defines the rule. + properties: + - name: pattern + type: String + description: | + Pattern defining the regular expression. + Its syntax (https://github.com/google/re2/wiki/Syntax) can be found under the google/re2 repository on GitHub. + required: true + - name: groupIndexes + type: Array + description: | + The index of the submatch to extract as findings. When not specified, the entire match is returned. No more than 3 may be included. + item_type: + type: Integer + - name: excludeInfoTypes + type: NestedObject + description: Set of infoTypes for which findings would affect this rule. + properties: + - name: infoTypes + type: Array + description: | + If a finding is matched by any of the infoType detectors listed here, the finding will be excluded from the scan results. + required: true + item_type: + type: NestedObject + properties: + - name: name + type: String + description: | + Name of the information type. Either a name of your choosing when creating a CustomInfoType, or one of the names listed + at https://cloud.google.com/dlp/docs/infotypes-reference when specifying a built-in type. + required: true + - name: version + type: String + description: | + Version name for this InfoType. + - name: sensitivityScore + type: NestedObject + description: | + Optional custom sensitivity for this InfoType. This only applies to data profiling. + properties: + - name: score + type: Enum + description: | + The sensitivity score applied to the resource. + required: true + enum_values: + - SENSITIVITY_LOW + - SENSITIVITY_MODERATE + - SENSITIVITY_HIGH + - name: excludeByHotword + type: NestedObject + description: | + Drop if the hotword rule is contained in the proximate context. + For tabular data, the context includes the column name. + properties: + - name: hotwordRegex + type: NestedObject + description: Regular expression pattern defining what qualifies as a hotword. + required: true + properties: + - name: pattern + type: String + description: | + Pattern defining the regular expression. Its syntax + (https://github.com/google/re2/wiki/Syntax) can be found under the google/re2 repository on GitHub. + required: true + - name: groupIndexes + type: Array + description: | + The index of the submatch to extract as findings. When not specified, + the entire match is returned. No more than 3 may be included. + item_type: + type: Integer + - name: proximity + type: NestedObject + description: | + Proximity of the finding within which the entire hotword must reside. The total length of the window cannot + exceed 1000 characters. Note that the finding itself will be included in the window, so that hotwords may be + used to match substrings of the finding itself. For example, the certainty of a phone number regex + `(\d{3}) \d{3}-\d{4}` could be adjusted upwards if the area code is known to be the local area code of a company + office using the hotword regex `(xxx)`, where `xxx` is the area code in question. + required: true + properties: + - name: windowBefore + type: Integer + description: | + Number of characters before the finding to consider. + - name: windowAfter + type: Integer + description: | + Number of characters after the finding to consider. + - name: customInfoTypes + type: Array + description: | + Custom info types to be used. See https://cloud.google.com/dlp/docs/creating-custom-infotypes to learn more. + item_type: + type: NestedObject + properties: + - name: infoType + type: NestedObject + description: | + CustomInfoType can either be a new infoType, or an extension of built-in infoType, when the name matches one of existing + infoTypes and that infoType is specified in `info_types` field. Specifying the latter adds findings to the + one detected by the system. If built-in info type is not specified in `info_types` list then the name is + treated as a custom info type. + required: true + properties: + - name: name + type: String + description: | + Name of the information type. Either a name of your choosing when creating a CustomInfoType, or one of the names + listed at https://cloud.google.com/dlp/docs/infotypes-reference when specifying a built-in type. + required: true + - name: version + type: String + description: | + Version name for this InfoType. + - name: sensitivityScore + type: NestedObject + description: | + Optional custom sensitivity for this InfoType. This only applies to data profiling. + properties: + - name: score + type: Enum + description: | + The sensitivity score applied to the resource. + required: true + enum_values: + - SENSITIVITY_LOW + - SENSITIVITY_MODERATE + - SENSITIVITY_HIGH + - name: likelihood + type: Enum + description: | + Likelihood to return for this CustomInfoType. This base value can be altered by a detection rule if the finding meets the criteria + specified by the rule. + default_value: VERY_LIKELY + enum_values: + - VERY_UNLIKELY + - UNLIKELY + - POSSIBLE + - LIKELY + - VERY_LIKELY + - name: exclusionType + type: Enum + description: | + If set to EXCLUSION_TYPE_EXCLUDE this infoType will not cause a finding to be returned. It still can be used for rules matching. + enum_values: + - EXCLUSION_TYPE_EXCLUDE + - name: sensitivityScore + type: NestedObject + description: | + Optional custom sensitivity for this InfoType. This only applies to data profiling. + properties: + - name: score + type: Enum + description: | + The sensitivity score applied to the resource. + required: true + enum_values: + - SENSITIVITY_LOW + - SENSITIVITY_MODERATE + - SENSITIVITY_HIGH + - name: regex + type: NestedObject + description: Regular expression which defines the rule. + immutable: true + properties: + - name: pattern + type: String + description: | + Pattern defining the regular expression. + Its syntax (https://github.com/google/re2/wiki/Syntax) can be found under the google/re2 repository on GitHub. + required: true + - name: groupIndexes + type: Array + description: | + The index of the submatch to extract as findings. When not specified, the entire match is returned. No more than 3 may be included. + item_type: + type: Integer + - name: dictionary + type: NestedObject + description: Dictionary which defines the rule. + immutable: true + properties: + - name: wordList + type: NestedObject + description: List of words or phrases to search for. + properties: + - name: words + type: Array + description: | + Words or phrases defining the dictionary. The dictionary must contain at least one + phrase and every phrase must contain at least 2 characters that are letters or digits. + required: true + item_type: + type: String + - name: cloudStoragePath + type: NestedObject + description: Newline-delimited file of words in Cloud Storage. Only a single file is accepted. + properties: + - name: path + type: String + description: | + A url representing a file or path (no wildcards) in Cloud Storage. Example: `gs://[BUCKET_NAME]/dictionary.txt` + required: true + - name: surrogateType + type: NestedObject + description: | + Message for detecting output from deidentification transformations that support reversing. + immutable: true + send_empty_value: true + allow_empty_object: true + properties: [] + - name: storedType + type: NestedObject + description: A reference to a StoredInfoType to use with scanning. + immutable: true + properties: + - name: name + type: String + description: | + Resource name of the requested StoredInfoType, for example `organizations/433245324/storedInfoTypes/432452342` + or `projects/project-id/storedInfoTypes/432452342`. + required: true + - name: rules + type: Array + description: | + Policies to apply, based on the findings returned by inspection. The first rule to match applies. + required: true + item_type: + type: NestedObject + properties: + - name: conditions + type: Array + description: | + List of conditions that must be met for this rule to apply. + item_type: + type: NestedObject + properties: + - name: infoTypeCondition + type: NestedObject + description: | + A condition based on info types. + properties: + - name: infoTypes + type: NestedObject + description: | + List of info types to match. + properties: + - name: infoTypeNames + type: Array + description: | + List of info type names. + required: true + item_type: + type: String + - name: anyInfoType + type: NestedObject + description: | + Match any info type. + send_empty_value: true + allow_empty_object: true + properties: [] + - name: minCount + type: Integer + description: | + The minimum number of findings required for this condition to be met. Defaults to 1. + - name: action + type: NestedObject + description: | + The action to take if the rule matches. + required: true + properties: + - name: returnVerdict + type: Enum + description: | + If set, the verdict will be returned to the user. + Possible values: ["ALLOW", "BLOCK"] + enum_values: + - ALLOW + - BLOCK + - name: unsupportedFileType + type: NestedObject + description: | + Action to take if the content is an unsupported file type. + properties: + - name: returnVerdict + type: Enum + description: | + If set, the verdict will be returned to the user. + Possible values: ["ALLOW", "BLOCK"] + enum_values: + - ALLOW + - BLOCK + - name: inputTooLarge + type: NestedObject + description: | + Action to take if the content is a supported file type but is too large to be scanned. + properties: + - name: returnVerdict + type: Enum + description: | + If set, the verdict will be returned to the user. + Possible values: ["ALLOW", "BLOCK"] + enum_values: + - ALLOW + - BLOCK + - name: failedToScanSupportedFileType + type: NestedObject + description: | + Action to take if the content is a supported file type and size but fails to be scanned, for example because the file is encrypted or corrupted. + properties: + - name: returnVerdict + type: Enum + description: | + If set, the verdict will be returned to the user. + Possible values: ["ALLOW", "BLOCK"] + enum_values: + - ALLOW + - BLOCK + - name: defaultAction + type: NestedObject + description: | + Action to take if the content is scanned and no rules match. Defaults to returning an ALLOW verdict if not set. + properties: + - name: returnVerdict + type: Enum + description: | + If set, the verdict will be returned to the user. + Possible values: ["ALLOW", "BLOCK"] + enum_values: + - ALLOW + - BLOCK + - name: loggingConfigs + type: Array + description: | + Log the actions taken by the content policy to external systems. + item_type: + type: NestedObject + properties: + - name: logToBigQuery + type: NestedObject + description: | + Log actions to BigQuery. + properties: + - name: projectId + type: String + description: | + The project ID of the BigQuery table to log to. + required: true + - name: datasetId + type: String + description: | + The dataset ID of the BigQuery table to log to. + required: true + - name: tableId + type: String + description: | + The table ID of the BigQuery table to log to. + required: true + - name: errors + type: Array + description: | + Output only. A stream of errors encountered when the policy was applied. Output only field. Will return the last 100 errors. + output: true + item_type: + type: NestedObject + properties: + - name: status + type: NestedObject + description: Detailed error codes and messages. + output: true + properties: + - name: code + type: Integer + description: The status code, which should be an enum value of google.rpc.Code. + output: true + - name: message + type: String + description: A developer-facing error message, which should be in English. + output: true + - name: details + type: Array + description: A list of messages that carry the error details. + output: true + custom_flatten: templates/terraform/custom_flatten/dlp_discovery_config_error_details.tmpl + item_type: + type: KeyValuePairs + - name: timestamps + type: Array + description: The times the error occurred. List includes the oldest timestamp and the last 9 timestamps. + output: true + item_type: + type: String + - name: details + type: String + description: Extra details for users to understand what went wrong. + output: true + - name: createTime + type: String + description: Output only. The creation timestamp of a ContentPolicy. + output: true + - name: updateTime + type: String + description: Output only. The last update timestamp of a ContentPolicy. + output: true diff --git a/mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl b/mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl new file mode 100644 index 000000000000..29805e018a11 --- /dev/null +++ b/mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl @@ -0,0 +1,44 @@ +{{/* + The license inside this block applies to this file + 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. +*/ -}} + config := meta.(*transport_tpg.Config) + + // Custom import to handle parent possibilities + if err := tpgresource.ParseImportId([]string{"(?P.+)"}, d, config); err != nil { + return nil, err + } + parts := strings.Split(d.Get("name").(string), "/") + if len(parts) == 6 { + if err := d.Set("name", parts[5]); err != nil { + return nil, fmt.Errorf("Error setting name: %s", err) + } + } else if len(parts) == 4 { + if err := d.Set("name", parts[3]); err != nil { + return nil, fmt.Errorf("Error setting name: %s", err) + } + } else { + return nil, fmt.Errorf("Unexpected import id: %s, expected form {{"{{"}}parent{{"}}"}}/contentPolicies/{{"{{"}}name{{"}}"}}", d.Get("name").(string)) + } + // Remove "/contentPolicies/name" from the id + parts = parts[:len(parts)-2] + if err := d.Set("parent", strings.Join(parts, "/")); err != nil { + return nil, fmt.Errorf("Error setting parent: %s", err) + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}parent{{"}}"}}/contentPolicies/{{"{{"}}name{{"}}"}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil diff --git a/mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_basic.tf.tmpl b/mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_basic.tf.tmpl new file mode 100644 index 000000000000..03c609dbc555 --- /dev/null +++ b/mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_basic.tf.tmpl @@ -0,0 +1,29 @@ +resource "google_data_loss_prevention_content_policy" "{{$.PrimaryResourceId}}" { + parent = "projects/{{index $.TestEnvVars "project"}}/locations/{{index $.TestEnvVars "location"}}" + display_name = "tf-test-dlp-content-policy-basic" + + inspect_config { + info_types { + name = "EMAIL_ADDRESS" + } + min_likelihood = "POSSIBLE" + } + + rules { + conditions { + info_type_condition { + info_types { + info_type_names = ["EMAIL_ADDRESS"] + } + min_count = 1 + } + } + action { + return_verdict = "BLOCK" + } + } + + default_action { + return_verdict = "ALLOW" + } +} diff --git a/mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_full.tf.tmpl b/mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_full.tf.tmpl new file mode 100644 index 000000000000..97298172c28f --- /dev/null +++ b/mmv1/templates/terraform/samples/services/dlp/dlp_content_policy_full.tf.tmpl @@ -0,0 +1,304 @@ +resource "google_data_loss_prevention_content_policy" "{{$.PrimaryResourceId}}" { + parent = "projects/{{index $.TestEnvVars "project"}}/locations/{{index $.TestEnvVars "location"}}" + display_name = "tf-test-dlp-content-policy-full" + + inspect_config { + content_options = ["CONTENT_TEXT", "CONTENT_IMAGE"] + include_quote = true + exclude_info_types = false + min_likelihood = "LIKELY" + + info_types { + name = "PHONE_NUMBER" + sensitivity_score { + score = "SENSITIVITY_LOW" + } + } + + info_types { + name = "EMAIL_ADDRESS" + } + + info_types { + name = "PERSON_NAME" + version = "latest" + } + + info_types { + name = "FIRST_NAME" + } + + info_types { + name = "LAST_NAME" + } + + min_likelihood_per_info_type { + info_type { + name = "PERSON_NAME" + version = "latest" + } + min_likelihood = "LIKELY" + } + + custom_info_types { + info_type { + name = "MY_CUSTOM_TYPE" + version = "0.1" + sensitivity_score { + score = "SENSITIVITY_LOW" + } + } + sensitivity_score { + score = "SENSITIVITY_MODERATE" + } + likelihood = "UNLIKELY" + exclusion_type = "EXCLUSION_TYPE_EXCLUDE" + regex { + pattern = "test.*" + group_indexes = [0] + } + } + + custom_info_types { + info_type { + name = "MY_WORD_LIST_TYPE" + } + likelihood = "POSSIBLE" + dictionary { + word_list { + words = ["badword1", "badword2"] + } + } + } + + custom_info_types { + info_type { + name = "MY_STORAGE_TYPE" + } + likelihood = "POSSIBLE" + dictionary { + cloud_storage_path { + path = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}" + } + } + } + + custom_info_types { + info_type { + name = "MY_STORED_TYPE" + } + likelihood = "POSSIBLE" + stored_type { + name = google_data_loss_prevention_stored_info_type.basic.id + } + } + + limits { + max_findings_per_item = 10 + max_findings_per_request = 50 + max_findings_per_info_type { + max_findings = 10 + info_type { + name = "PERSON_NAME" + version = "latest" + sensitivity_score { + score = "SENSITIVITY_HIGH" + } + } + } + } + + rule_set { + info_types { + name = "PHONE_NUMBER" + sensitivity_score { + score = "SENSITIVITY_LOW" + } + } + rules { + exclusion_rule { + matching_type = "MATCHING_TYPE_FULL_MATCH" + exclude_info_types { + info_types { + name = "PERSON_NAME" + version = "latest" + sensitivity_score { + score = "SENSITIVITY_LOW" + } + } + } + } + } + } + + rule_set { + info_types { + name = "EMAIL_ADDRESS" + } + rules { + exclusion_rule { + matching_type = "MATCHING_TYPE_PARTIAL_MATCH" + regex { + pattern = ".*@example\\.com" + group_indexes = [0] + } + } + } + } + + rule_set { + info_types { + name = "PERSON_NAME" + version = "latest" + } + rules { + exclusion_rule { + matching_type = "MATCHING_TYPE_PARTIAL_MATCH" + dictionary { + word_list { + words = ["test"] + } + } + } + } + rules { + exclusion_rule { + matching_type = "MATCHING_TYPE_PARTIAL_MATCH" + dictionary { + cloud_storage_path { + path = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.object.name}" + } + } + } + } + } + + rule_set { + info_types { + name = "FIRST_NAME" + } + rules { + exclusion_rule { + matching_type = "MATCHING_TYPE_FULL_MATCH" + exclude_by_hotword { + hotword_regex { + pattern = "zone.*" + group_indexes = [0] + } + proximity { + window_before = 1 + window_after = 2 + } + } + } + } + } + + rule_set { + info_types { + name = "LAST_NAME" + } + rules { + hotword_rule { + hotword_regex { + pattern = "patient.*" + group_indexes = [0] + } + proximity { + window_before = 1 + window_after = 2 + } + likelihood_adjustment { + fixed_likelihood = "VERY_LIKELY" + } + } + } + } + } + + rules { + conditions { + info_type_condition { + any_info_type {} + min_count = 2 + } + } + action { + return_verdict = "BLOCK" + } + } + + unsupported_file_type { + return_verdict = "ALLOW" + } + + input_too_large { + return_verdict = "ALLOW" + } + + failed_to_scan_supported_file_type { + return_verdict = "BLOCK" + } + + default_action { + return_verdict = "ALLOW" + } + + logging_configs { + log_to_big_query { + project_id = google_bigquery_table.default.project + dataset_id = google_bigquery_dataset.default.dataset_id + table_id = google_bigquery_table.default.table_id + } + } +} + +resource "google_data_loss_prevention_stored_info_type" "basic" { + parent = "projects/{{index $.TestEnvVars "project"}}/locations/{{index $.TestEnvVars "location"}}" + description = "Description for stored info type" + display_name = "tf-test-stored-info-type" + + regex { + pattern = ".*" + group_indexes = [0] + } +} + +resource "google_storage_bucket" "bucket" { + name = "{{index $.ResourceIdVars "bucket_name"}}" + location = "US" + uniform_bucket_level_access = true + force_destroy = true +} + +resource "google_storage_bucket_object" "object" { + name = "tf-test-wordlist" + bucket = google_storage_bucket.bucket.name + content = "word1\nword2\n" +} + +resource "google_bigquery_dataset" "default" { + dataset_id = "{{index $.ResourceIdVars "dataset_id"}}" + friendly_name = "tf-test-dataset" + description = "Description for the dataset created by terraform" + location = "US" + default_table_expiration_ms = 3600000 + delete_contents_on_destroy = true +} + +resource "google_bigquery_table" "default" { + dataset_id = google_bigquery_dataset.default.dataset_id + table_id = "{{index $.ResourceIdVars "table_id"}}" + deletion_protection = false + + schema = < Date: Wed, 9 Sep 2026 14:51:27 -0700 Subject: [PATCH 2/2] Apply batched suggestions from code review Co-authored-by: Chris Hawk --- mmv1/products/dlp/ContentPolicy.yaml | 13 +++++++++---- .../dlp_content_policy_import.go.tmpl | 14 +++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mmv1/products/dlp/ContentPolicy.yaml b/mmv1/products/dlp/ContentPolicy.yaml index 510cf2120afb..fe712b4bd1b0 100644 --- a/mmv1/products/dlp/ContentPolicy.yaml +++ b/mmv1/products/dlp/ContentPolicy.yaml @@ -26,6 +26,8 @@ delete_url: '{{parent}}/contentPolicies/{{name}}' update_mask: true update_verb: PATCH id_format: '{{parent}}/contentPolicies/{{name}}' +import_format: + - '{{parent}}/contentPolicies/{{name}}' timeouts: insert_minutes: 20 update_minutes: 20 @@ -765,7 +767,7 @@ properties: item_type: type: NestedObject properties: - - name: status + - name: details type: NestedObject description: Detailed error codes and messages. output: true @@ -791,10 +793,13 @@ properties: output: true item_type: type: String - - name: details - type: String - description: Extra details for users to understand what went wrong. + - name: extraInfo + type: Enum + description: Additional information about the error. output: true + enum_values: + - IMAGE_SCAN_UNAVAILABLE_IN_REGION + - FILE_STORE_CLUSTER_UNSUPPORTED - name: createTime type: String description: Output only. The creation timestamp of a ContentPolicy. diff --git a/mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl b/mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl index 29805e018a11..7914221382d6 100644 --- a/mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl +++ b/mmv1/templates/terraform/custom_import/dlp_content_policy_import.go.tmpl @@ -17,27 +17,23 @@ return nil, err } parts := strings.Split(d.Get("name").(string), "/") - if len(parts) == 6 { + if len(parts) == 6 && parts[4] == "contentPolicies" { if err := d.Set("name", parts[5]); err != nil { - return nil, fmt.Errorf("Error setting name: %s", err) - } - } else if len(parts) == 4 { - if err := d.Set("name", parts[3]); err != nil { - return nil, fmt.Errorf("Error setting name: %s", err) + return nil, fmt.Errorf("error setting name: %w", err) } } else { - return nil, fmt.Errorf("Unexpected import id: %s, expected form {{"{{"}}parent{{"}}"}}/contentPolicies/{{"{{"}}name{{"}}"}}", d.Get("name").(string)) + return nil, fmt.Errorf("unexpected import id: %s, expected form {{"{{"}}parent{{"}}"}}/contentPolicies/{{"{{"}}name{{"}}"}}", d.Get("name").(string)) } // Remove "/contentPolicies/name" from the id parts = parts[:len(parts)-2] if err := d.Set("parent", strings.Join(parts, "/")); err != nil { - return nil, fmt.Errorf("Error setting parent: %s", err) + return nil, fmt.Errorf("error setting parent: %w", err) } // Replace import id for the resource id id, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}parent{{"}}"}}/contentPolicies/{{"{{"}}name{{"}}"}}") if err != nil { - return nil, fmt.Errorf("Error constructing id: %s", err) + return nil, fmt.Errorf("error constructing id: %w", err) } d.SetId(id)