From 560eeb15aa5da2664c9e40dbef6f3abae3556e18 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Mon, 20 Sep 2021 20:06:49 +0000 Subject: [PATCH] Added org policy policy resource. (#5199) * Added org policy policy resource. * Added additional comments. * Allow resources to use the normal terraform ID process by default - DCL by override. * Added a way to expand and flatten between terraform strings and dcl booleans. * Updated GA version of policy.yaml. * Ran make upgrade-dcl (and added missing tab to tf go.mod). * Added NO_SWEEPER override for orgpolicy policy and formatting for id construction errors. Co-authored-by: Nathan Mckinley Signed-off-by: Modular Magician --- .changelog/5199.txt | 3 + go.mod | 2 +- go.sum | 4 + google/config.go | 8 +- google/expanders.go | 17 + google/flatteners.go | 13 + google/orgpolicy_utils.go | 28 + google/provider.go | 5 + google/provider_dcl_client_creation.go | 40 ++ google/provider_dcl_endpoints.go | 24 + google/resource_assured_workloads_workload.go | 2 +- google/resource_compute_firewall_policy.go | 2 +- ...rce_compute_firewall_policy_association.go | 2 +- .../resource_compute_firewall_policy_rule.go | 2 +- google/resource_compute_forwarding_rule.go | 2 +- ...resource_compute_global_forwarding_rule.go | 2 +- google/resource_dataproc_workflow_template.go | 2 +- google/resource_eventarc_trigger.go | 2 +- google/resource_org_policy_policy.go | 504 ++++++++++++++++++ ...source_org_policy_policy_generated_test.go | 376 +++++++++++++ ...resource_org_policy_policy_sweeper_test.go | 72 +++ ...resource_privateca_certificate_template.go | 2 +- .../docs/r/org_policy_policy.html.markdown | 240 +++++++++ website/google.erb | 16 + 24 files changed, 1357 insertions(+), 13 deletions(-) create mode 100644 .changelog/5199.txt create mode 100644 google/flatteners.go create mode 100644 google/orgpolicy_utils.go create mode 100644 google/resource_org_policy_policy.go create mode 100644 google/resource_org_policy_policy_generated_test.go create mode 100644 google/resource_org_policy_policy_sweeper_test.go create mode 100644 website/docs/r/org_policy_policy.html.markdown diff --git a/.changelog/5199.txt b/.changelog/5199.txt new file mode 100644 index 00000000000..a1fc47af51c --- /dev/null +++ b/.changelog/5199.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`google_org_policy_policy` +``` diff --git a/go.mod b/go.mod index 48782931571..ebfa6103b6d 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/hashicorp/terraform-provider-google require ( cloud.google.com/go/bigtable v1.10.1 - github.com/GoogleCloudPlatform/declarative-resource-client-library v0.0.0-20210914194833-2626db3d194d + github.com/GoogleCloudPlatform/declarative-resource-client-library v0.0.0-20210918014849-ef8e2b337288 github.com/apparentlymart/go-cidr v1.1.0 github.com/client9/misspell v0.3.4 github.com/davecgh/go-spew v1.1.1 diff --git a/go.sum b/go.sum index 2ed02d5293d..32406282857 100644 --- a/go.sum +++ b/go.sum @@ -1405,3 +1405,7 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +github.com/GoogleCloudPlatform/declarative-resource-client-library v0.0.0-20210918014849-ef8e2b337288 h1:mMwzB+vf5cvKCKAl1RocM5CqHhr8NtEKpP8ioSDVhO4= +github.com/GoogleCloudPlatform/declarative-resource-client-library v0.0.0-20210918014849-ef8e2b337288/go.mod h1:oEeBHikdF/NrnUy0ornVaY1OT+jGvTqm+LQS0+ZDKzU= +github.com/GoogleCloudPlatform/declarative-resource-client-library v0.0.0-20210918014849-ef8e2b337288 h1:mMwzB+vf5cvKCKAl1RocM5CqHhr8NtEKpP8ioSDVhO4= +github.com/GoogleCloudPlatform/declarative-resource-client-library v0.0.0-20210918014849-ef8e2b337288/go.mod h1:oEeBHikdF/NrnUy0ornVaY1OT+jGvTqm+LQS0+ZDKzU= diff --git a/google/config.go b/google/config.go index 3389bf0d49c..8a288c6246a 100644 --- a/google/config.go +++ b/google/config.go @@ -168,9 +168,11 @@ type Config struct { // start DCLBasePaths // dataprocBasePath is implemented in mm - AssuredWorkloadsBasePath string - EventarcBasePath string - GkeHubBasePath string + AssuredWorkloadsBasePath string + CloudResourceManagerBasePath string + EventarcBasePath string + GkeHubBasePath string + OrgPolicyBasePath string } const AccessApprovalBasePathKey = "AccessApproval" diff --git a/google/expanders.go b/google/expanders.go index 677773cf378..43b113a8f48 100644 --- a/google/expanders.go +++ b/google/expanders.go @@ -46,3 +46,20 @@ func convertIntegerArr(v []interface{}) []int64 { } return vi } + +// Returns the DCL representation of a three-state boolean value represented by a string in terraform. +func expandEnumBool(v interface{}) *bool { + s, ok := v.(string) + if !ok { + return nil + } + switch s { + case "TRUE": + b := true + return &b + case "FALSE": + b := false + return &b + } + return nil +} diff --git a/google/flatteners.go b/google/flatteners.go new file mode 100644 index 00000000000..707d823b112 --- /dev/null +++ b/google/flatteners.go @@ -0,0 +1,13 @@ +package google + +// Returns the terraform representation of a three-state boolean value represented by a pointer to bool in DCL. +func flattenEnumBool(v interface{}) string { + b, ok := v.(*bool) + if !ok || b == nil { + return "" + } + if *b { + return "TRUE" + } + return "FALSE" +} diff --git a/google/orgpolicy_utils.go b/google/orgpolicy_utils.go new file mode 100644 index 00000000000..56c680456f0 --- /dev/null +++ b/google/orgpolicy_utils.go @@ -0,0 +1,28 @@ +package google + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// OrgPolicyPolicy has a custom import method because the parent field needs to allow an additional forward slash +// to represent the type of parent (e.g. projects/{project_id}). +func resourceOrgPolicyPolicyCustomImport(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + if err := parseImportId([]string{ + "^(?P[^/]+/?[^/]*)/policies/(?P[^/]+)", + "^(?P[^/]+/?[^/]*)/(?P[^/]+)", + }, d, config); err != nil { + return err + } + + // Replace import id for the resource id + id, err := replaceVarsRecursive(d, config, "{{parent}}/policies/{{name}}", false, 0) + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return nil +} diff --git a/google/provider.go b/google/provider.go index 0706db3d252..667ca10cd82 100644 --- a/google/provider.go +++ b/google/provider.go @@ -695,8 +695,10 @@ func Provider() *schema.Provider { // dcl AssuredWorkloadsEndpointEntryKey: AssuredWorkloadsEndpointEntry, + CloudResourceManagerEndpointEntryKey: CloudResourceManagerEndpointEntry, EventarcEndpointEntryKey: EventarcEndpointEntry, GkeHubFeatureCustomEndpointEntryKey: GkeHubFeatureCustomEndpointEntry, + OrgPolicyEndpointEntryKey: OrgPolicyEndpointEntry, PrivatecaCertificateTemplateEndpointEntryKey: PrivatecaCertificateTemplateCustomEndpointEntry, }, @@ -1204,6 +1206,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_compute_firewall_policy_rule": resourceComputeFirewallPolicyRule(), "google_dataproc_workflow_template": resourceDataprocWorkflowTemplate(), "google_eventarc_trigger": resourceEventarcTrigger(), + "google_org_policy_policy": resourceOrgPolicyPolicy(), "google_privateca_certificate_template": resourcePrivatecaCertificateTemplate(), }, // ------------------------------------ @@ -1431,8 +1434,10 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr // dcl config.AssuredWorkloadsBasePath = d.Get(AssuredWorkloadsEndpointEntryKey).(string) + config.CloudResourceManagerBasePath = d.Get(CloudResourceManagerEndpointEntryKey).(string) config.EventarcBasePath = d.Get(EventarcEndpointEntryKey).(string) config.GkeHubBasePath = d.Get(GkeHubFeatureCustomEndpointEntryKey).(string) + config.OrgPolicyBasePath = d.Get(OrgPolicyEndpointEntryKey).(string) config.PrivatecaBasePath = d.Get(PrivatecaCertificateTemplateEndpointEntryKey).(string) stopCtx, ok := schema.StopContext(ctx) diff --git a/google/provider_dcl_client_creation.go b/google/provider_dcl_client_creation.go index 7dfd098ee41..d0ea0d62e1a 100644 --- a/google/provider_dcl_client_creation.go +++ b/google/provider_dcl_client_creation.go @@ -19,9 +19,11 @@ import ( dcl "github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl" assuredworkloads "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/assuredworkloads" + cloudresourcemanager "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/cloudresourcemanager" compute "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/compute" dataproc "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/dataproc" eventarc "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/eventarc" + orgpolicy "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/orgpolicy" privateca "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/privateca" ) @@ -44,6 +46,25 @@ func NewDCLAssuredWorkloadsClient(config *Config, userAgent, billingProject stri return assuredworkloads.NewClient(dclConfig) } +func NewDCLCloudResourceManagerClient(config *Config, userAgent, billingProject string) *cloudresourcemanager.Client { + configOptions := []dcl.ConfigOption{ + dcl.WithHTTPClient(config.client), + dcl.WithUserAgent(userAgent), + dcl.WithLogger(dclLogger{}), + dcl.WithBasePath(config.CloudResourceManagerBasePath), + } + + if config.UserProjectOverride { + configOptions = append(configOptions, dcl.WithUserProjectOverride()) + if billingProject != "" { + configOptions = append(configOptions, dcl.WithBillingProject(billingProject)) + } + } + + dclConfig := dcl.NewConfig(configOptions...) + return cloudresourcemanager.NewClient(dclConfig) +} + func NewDCLComputeClient(config *Config, userAgent, billingProject string) *compute.Client { configOptions := []dcl.ConfigOption{ dcl.WithHTTPClient(config.client), @@ -101,6 +122,25 @@ func NewDCLEventarcClient(config *Config, userAgent, billingProject string) *eve return eventarc.NewClient(dclConfig) } +func NewDCLOrgPolicyClient(config *Config, userAgent, billingProject string) *orgpolicy.Client { + configOptions := []dcl.ConfigOption{ + dcl.WithHTTPClient(config.client), + dcl.WithUserAgent(userAgent), + dcl.WithLogger(dclLogger{}), + dcl.WithBasePath(config.OrgPolicyBasePath), + } + + if config.UserProjectOverride { + configOptions = append(configOptions, dcl.WithUserProjectOverride()) + if billingProject != "" { + configOptions = append(configOptions, dcl.WithBillingProject(billingProject)) + } + } + + dclConfig := dcl.NewConfig(configOptions...) + return orgpolicy.NewClient(dclConfig) +} + func NewDCLPrivatecaClient(config *Config, userAgent, billingProject string) *privateca.Client { configOptions := []dcl.ConfigOption{ dcl.WithHTTPClient(config.client), diff --git a/google/provider_dcl_endpoints.go b/google/provider_dcl_endpoints.go index c55cb3551ca..e33e41ea32e 100644 --- a/google/provider_dcl_endpoints.go +++ b/google/provider_dcl_endpoints.go @@ -31,6 +31,15 @@ var AssuredWorkloadsEndpointEntry = &schema.Schema{ }, ""), } +var CloudResourceManagerEndpointEntryKey = "cloud_resource_manager_custom_endpoint" +var CloudResourceManagerEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_CLOUD_RESOURCE_MANAGER_CUSTOM_ENDPOINT", + }, ""), +} + var ComputeEndpointEntryKey = "compute_custom_endpoint" var ComputeEndpointEntry = &schema.Schema{ Type: schema.TypeString, @@ -49,6 +58,15 @@ var EventarcEndpointEntry = &schema.Schema{ }, ""), } +var OrgPolicyEndpointEntryKey = "org_policy_custom_endpoint" +var OrgPolicyEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_ORG_POLICY_CUSTOM_ENDPOINT", + }, ""), +} + var PrivatecaEndpointEntryKey = "privateca_custom_endpoint" var PrivatecaEndpointEntry = &schema.Schema{ Type: schema.TypeString, @@ -60,18 +78,24 @@ var PrivatecaEndpointEntry = &schema.Schema{ //Add new values to config.go.erb config object declaration //AssuredWorkloadsBasePath string +//CloudResourceManagerBasePath string //ComputeBasePath string //EventarcBasePath string +//OrgPolicyBasePath string //PrivatecaBasePath string //Add new values to provider.go.erb schema initialization // AssuredWorkloadsEndpointEntryKey: AssuredWorkloadsEndpointEntry, +// CloudResourceManagerEndpointEntryKey: CloudResourceManagerEndpointEntry, // ComputeEndpointEntryKey: ComputeEndpointEntry, // EventarcEndpointEntryKey: EventarcEndpointEntry, +// OrgPolicyEndpointEntryKey: OrgPolicyEndpointEntry, // PrivatecaEndpointEntryKey: PrivatecaEndpointEntry, //Add new values to provider.go.erb - provider block read // config.AssuredWorkloadsBasePath = d.Get(AssuredWorkloadsEndpointEntryKey).(string) +// config.CloudResourceManagerBasePath = d.Get(CloudResourceManagerEndpointEntryKey).(string) // config.ComputeBasePath = d.Get(ComputeEndpointEntryKey).(string) // config.EventarcBasePath = d.Get(EventarcEndpointEntryKey).(string) +// config.OrgPolicyBasePath = d.Get(OrgPolicyEndpointEntryKey).(string) // config.PrivatecaBasePath = d.Get(PrivatecaEndpointEntryKey).(string) diff --git a/google/resource_assured_workloads_workload.go b/google/resource_assured_workloads_workload.go index 57595d275a0..6fb534937d9 100644 --- a/google/resource_assured_workloads_workload.go +++ b/google/resource_assured_workloads_workload.go @@ -209,7 +209,7 @@ func resourceAssuredWorkloadsWorkloadCreate(d *schema.ResourceData, meta interfa id, err := replaceVarsForId(d, config, "organizations/{{organization}}/locations/{{location}}/workloads/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_compute_firewall_policy.go b/google/resource_compute_firewall_policy.go index 2ae6a6e587f..45506cfdb49 100644 --- a/google/resource_compute_firewall_policy.go +++ b/google/resource_compute_firewall_policy.go @@ -122,7 +122,7 @@ func resourceComputeFirewallPolicyCreate(d *schema.ResourceData, meta interface{ id, err := replaceVars(d, config, "locations/global/firewallPolicies/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_compute_firewall_policy_association.go b/google/resource_compute_firewall_policy_association.go index aaf8f519589..7ea771e4a30 100644 --- a/google/resource_compute_firewall_policy_association.go +++ b/google/resource_compute_firewall_policy_association.go @@ -86,7 +86,7 @@ func resourceComputeFirewallPolicyAssociationCreate(d *schema.ResourceData, meta id, err := replaceVarsForId(d, config, "locations/global/firewallPolicies/{{firewall_policy}}/associations/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_compute_firewall_policy_rule.go b/google/resource_compute_firewall_policy_rule.go index f9d8094ca71..397785b752e 100644 --- a/google/resource_compute_firewall_policy_rule.go +++ b/google/resource_compute_firewall_policy_rule.go @@ -192,7 +192,7 @@ func resourceComputeFirewallPolicyRuleCreate(d *schema.ResourceData, meta interf id, err := replaceVarsForId(d, config, "locations/global/firewallPolicies/{{firewall_policy}}/rules/{{priority}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_compute_forwarding_rule.go b/google/resource_compute_forwarding_rule.go index 4ea059cb55b..5bc668828b2 100644 --- a/google/resource_compute_forwarding_rule.go +++ b/google/resource_compute_forwarding_rule.go @@ -259,7 +259,7 @@ func resourceComputeForwardingRuleCreate(d *schema.ResourceData, meta interface{ id, err := replaceVarsForId(d, config, "projects/{{project}}/regions/{{region}}/forwardingRules/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_compute_global_forwarding_rule.go b/google/resource_compute_global_forwarding_rule.go index 0413e662458..e7aa7d1521e 100644 --- a/google/resource_compute_global_forwarding_rule.go +++ b/google/resource_compute_global_forwarding_rule.go @@ -222,7 +222,7 @@ func resourceComputeGlobalForwardingRuleCreate(d *schema.ResourceData, meta inte id, err := replaceVarsForId(d, config, "projects/{{project}}/global/forwardingRules/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_dataproc_workflow_template.go b/google/resource_dataproc_workflow_template.go index 872e867eae9..b427083a745 100644 --- a/google/resource_dataproc_workflow_template.go +++ b/google/resource_dataproc_workflow_template.go @@ -1735,7 +1735,7 @@ func resourceDataprocWorkflowTemplateCreate(d *schema.ResourceData, meta interfa id, err := replaceVarsForId(d, config, "projects/{{project}}/locations/{{location}}/workflowTemplates/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_eventarc_trigger.go b/google/resource_eventarc_trigger.go index 4ef35645557..09b7aadbf43 100644 --- a/google/resource_eventarc_trigger.go +++ b/google/resource_eventarc_trigger.go @@ -253,7 +253,7 @@ func resourceEventarcTriggerCreate(d *schema.ResourceData, meta interface{}) err id, err := replaceVarsForId(d, config, "projects/{{project}}/locations/{{location}}/triggers/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/google/resource_org_policy_policy.go b/google/resource_org_policy_policy.go new file mode 100644 index 00000000000..2d09ede5011 --- /dev/null +++ b/google/resource_org_policy_policy.go @@ -0,0 +1,504 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: DCL *** +// +// ---------------------------------------------------------------------------- +// +// This file is managed by Magic Modules (https://github.com/GoogleCloudPlatform/magic-modules) +// and is based on the DCL (https://github.com/GoogleCloudPlatform/declarative-resource-client-library). +// Changes will need to be made to the DCL or Magic Modules instead of here. +// +// We are not currently able to accept contributions to this file. If changes +// are required, please file an issue at https://github.com/hashicorp/terraform-provider-google/issues/new/choose +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + dcl "github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl" + orgpolicy "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/orgpolicy" +) + +func resourceOrgPolicyPolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceOrgPolicyPolicyCreate, + Read: resourceOrgPolicyPolicyRead, + Update: resourceOrgPolicyPolicyUpdate, + Delete: resourceOrgPolicyPolicyDelete, + + Importer: &schema.ResourceImporter{ + State: resourceOrgPolicyPolicyImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Immutable. The resource name of the Policy. Must be one of the following forms, where constraint_name is the name of the constraint which this Policy configures: * `projects/{project_number}/policies/{constraint_name}` * `folders/{folder_id}/policies/{constraint_name}` * `organizations/{organization_id}/policies/{constraint_name}` For example, \"projects/123/policies/compute.disableSerialPortAccess\". Note: `projects/{project_id}/policies/{constraint_name}` is also an acceptable name for API requests, but responses will return the name using the equivalent project number.", + }, + + "parent": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: "The parent of the resource.", + }, + + "spec": { + Type: schema.TypeList, + Optional: true, + Description: "Basic information about the Organization Policy.", + MaxItems: 1, + Elem: OrgPolicyPolicySpecSchema(), + }, + }, + } +} + +func OrgPolicyPolicySpecSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "inherit_from_parent": { + Type: schema.TypeBool, + Optional: true, + Description: "Determines the inheritance behavior for this `Policy`. If `inherit_from_parent` is true, PolicyRules set higher up in the hierarchy (up to the closest root) are inherited and present in the effective policy. If it is false, then no rules are inherited, and this Policy becomes the new root for evaluation. This field can be set only for Policies which configure list constraints.", + }, + + "reset": { + Type: schema.TypeBool, + Optional: true, + Description: "Ignores policies set above this resource and restores the `constraint_default` enforcement behavior of the specific `Constraint` at this resource. This field can be set in policies for either list or boolean constraints. If set, `rules` must be empty and `inherit_from_parent` must be set to false.", + }, + + "rules": { + Type: schema.TypeList, + Optional: true, + Description: "Up to 10 PolicyRules are allowed. In Policies for boolean constraints, the following requirements apply: - There must be one and only one PolicyRule where condition is unset. - BooleanPolicyRules with conditions must set `enforced` to the opposite of the PolicyRule without a condition. - During policy evaluation, PolicyRules with conditions that are true for a target resource take precedence.", + Elem: OrgPolicyPolicySpecRulesSchema(), + }, + + "etag": { + Type: schema.TypeString, + Computed: true, + Description: "An opaque tag indicating the current version of the `Policy`, used for concurrency control. This field is ignored if used in a `CreatePolicy` request. When the `Policy` is returned from either a `GetPolicy` or a `ListPolicies` request, this `etag` indicates the version of the current `Policy` to use when executing a read-modify-write loop. When the `Policy` is returned from a `GetEffectivePolicy` request, the `etag` will be unset.", + }, + + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: "Output only. The time stamp this was previously updated. This represents the last time a call to `CreatePolicy` or `UpdatePolicy` was made for that `Policy`.", + }, + }, + } +} + +func OrgPolicyPolicySpecRulesSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow_all": { + Type: schema.TypeString, + Optional: true, + Description: "Setting this to true means that all values are allowed. This field can be set only in Policies for list constraints.", + }, + + "condition": { + Type: schema.TypeList, + Optional: true, + Description: "A condition which determines whether this rule is used in the evaluation of the policy. When set, the `expression` field in the `Expr' must include from 1 to 10 subexpressions, joined by the \"||\" or \"&&\" operators. Each subexpression must be of the form \"resource.matchTag('/tag_key_short_name, 'tag_value_short_name')\". or \"resource.matchTagId('tagKeys/key_id', 'tagValues/value_id')\". where key_name and value_name are the resource names for Label Keys and Values. These names are available from the Tag Manager Service. An example expression is: \"resource.matchTag('123456789/environment, 'prod')\". or \"resource.matchTagId('tagKeys/123', 'tagValues/456')\".", + MaxItems: 1, + Elem: OrgPolicyPolicySpecRulesConditionSchema(), + }, + + "deny_all": { + Type: schema.TypeString, + Optional: true, + Description: "Setting this to true means that all values are denied. This field can be set only in Policies for list constraints.", + }, + + "enforce": { + Type: schema.TypeString, + Optional: true, + Description: "If `true`, then the `Policy` is enforced. If `false`, then any configuration is acceptable. This field can be set only in Policies for boolean constraints.", + }, + + "values": { + Type: schema.TypeList, + Optional: true, + Description: "List of values to be used for this PolicyRule. This field can be set only in Policies for list constraints.", + MaxItems: 1, + Elem: OrgPolicyPolicySpecRulesValuesSchema(), + }, + }, + } +} + +func OrgPolicyPolicySpecRulesConditionSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "description": { + Type: schema.TypeString, + Optional: true, + Description: "Optional. Description of the expression. This is a longer text which describes the expression, e.g. when hovered over it in a UI.", + }, + + "expression": { + Type: schema.TypeString, + Optional: true, + Description: "Textual representation of an expression in Common Expression Language syntax.", + }, + + "location": { + Type: schema.TypeString, + Optional: true, + Description: "Optional. String indicating the location of the expression for error reporting, e.g. a file name and a position in the file.", + }, + + "title": { + Type: schema.TypeString, + Optional: true, + Description: "Optional. Title for the expression, i.e. a short string describing its purpose. This can be used e.g. in UIs which allow to enter the expression.", + }, + }, + } +} + +func OrgPolicyPolicySpecRulesValuesSchema() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed_values": { + Type: schema.TypeList, + Optional: true, + Description: "List of values allowed at this resource.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "denied_values": { + Type: schema.TypeList, + Optional: true, + Description: "List of values denied at this resource.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func resourceOrgPolicyPolicyCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := &orgpolicy.Policy{ + Name: dcl.String(d.Get("name").(string)), + Parent: dcl.String(d.Get("parent").(string)), + Spec: expandOrgPolicyPolicySpec(d.Get("spec")), + } + + id, err := obj.ID() + if err != nil { + return fmt.Errorf("error constructing id: %s", err) + } + d.SetId(id) + createDirective := CreateDirective + userAgent, err := generateUserAgentString(d, config.userAgent) + 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 + } + client := NewDCLOrgPolicyClient(config, userAgent, billingProject) + res, err := client.ApplyPolicy(context.Background(), obj, createDirective...) + + if _, ok := err.(dcl.DiffAfterApplyError); ok { + log.Printf("[DEBUG] Diff after apply returned from the DCL: %s", err) + } else if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error creating Policy: %s", err) + } + + log.Printf("[DEBUG] Finished creating Policy %q: %#v", d.Id(), res) + + return resourceOrgPolicyPolicyRead(d, meta) +} + +func resourceOrgPolicyPolicyRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := &orgpolicy.Policy{ + Name: dcl.String(d.Get("name").(string)), + Parent: dcl.String(d.Get("parent").(string)), + Spec: expandOrgPolicyPolicySpec(d.Get("spec")), + } + + userAgent, err := generateUserAgentString(d, config.userAgent) + 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 + } + client := NewDCLOrgPolicyClient(config, userAgent, billingProject) + res, err := client.GetPolicy(context.Background(), obj) + if err != nil { + resourceName := fmt.Sprintf("OrgPolicyPolicy %q", d.Id()) + return handleNotFoundDCLError(err, d, resourceName) + } + + if err = d.Set("name", res.Name); err != nil { + return fmt.Errorf("error setting name in state: %s", err) + } + if err = d.Set("parent", res.Parent); err != nil { + return fmt.Errorf("error setting parent in state: %s", err) + } + if err = d.Set("spec", flattenOrgPolicyPolicySpec(res.Spec)); err != nil { + return fmt.Errorf("error setting spec in state: %s", err) + } + + return nil +} +func resourceOrgPolicyPolicyUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := &orgpolicy.Policy{ + Name: dcl.String(d.Get("name").(string)), + Parent: dcl.String(d.Get("parent").(string)), + Spec: expandOrgPolicyPolicySpec(d.Get("spec")), + } + directive := UpdateDirective + userAgent, err := generateUserAgentString(d, config.userAgent) + 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 + } + client := NewDCLOrgPolicyClient(config, userAgent, billingProject) + res, err := client.ApplyPolicy(context.Background(), obj, directive...) + + if _, ok := err.(dcl.DiffAfterApplyError); ok { + log.Printf("[DEBUG] Diff after apply returned from the DCL: %s", err) + } else if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error updating Policy: %s", err) + } + + log.Printf("[DEBUG] Finished creating Policy %q: %#v", d.Id(), res) + + return resourceOrgPolicyPolicyRead(d, meta) +} + +func resourceOrgPolicyPolicyDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := &orgpolicy.Policy{ + Name: dcl.String(d.Get("name").(string)), + Parent: dcl.String(d.Get("parent").(string)), + Spec: expandOrgPolicyPolicySpec(d.Get("spec")), + } + + log.Printf("[DEBUG] Deleting Policy %q", d.Id()) + userAgent, err := generateUserAgentString(d, config.userAgent) + 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 + } + client := NewDCLOrgPolicyClient(config, userAgent, billingProject) + if err := client.DeletePolicy(context.Background(), obj); err != nil { + return fmt.Errorf("Error deleting Policy: %s", err) + } + + log.Printf("[DEBUG] Finished deleting Policy %q", d.Id()) + return nil +} + +func resourceOrgPolicyPolicyImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + + if err := resourceOrgPolicyPolicyCustomImport(d, config); err != nil { + return nil, fmt.Errorf("error encountered in import: %v", err) + } + + return []*schema.ResourceData{d}, nil +} + +func expandOrgPolicyPolicySpec(o interface{}) *orgpolicy.PolicySpec { + if o == nil { + return orgpolicy.EmptyPolicySpec + } + objArr := o.([]interface{}) + if len(objArr) == 0 { + return orgpolicy.EmptyPolicySpec + } + obj := objArr[0].(map[string]interface{}) + return &orgpolicy.PolicySpec{ + InheritFromParent: dcl.Bool(obj["inherit_from_parent"].(bool)), + Reset: dcl.Bool(obj["reset"].(bool)), + Rules: expandOrgPolicyPolicySpecRulesArray(obj["rules"]), + } +} + +func flattenOrgPolicyPolicySpec(obj *orgpolicy.PolicySpec) interface{} { + if obj == nil || obj.Empty() { + return nil + } + transformed := map[string]interface{}{ + "inherit_from_parent": obj.InheritFromParent, + "reset": obj.Reset, + "rules": flattenOrgPolicyPolicySpecRulesArray(obj.Rules), + "etag": obj.Etag, + "update_time": obj.UpdateTime, + } + + return []interface{}{transformed} + +} +func expandOrgPolicyPolicySpecRulesArray(o interface{}) []orgpolicy.PolicySpecRules { + if o == nil { + return nil + } + + objs := o.([]interface{}) + if len(objs) == 0 { + return nil + } + + items := make([]orgpolicy.PolicySpecRules, 0, len(objs)) + for _, item := range objs { + i := expandOrgPolicyPolicySpecRules(item) + items = append(items, *i) + } + + return items +} + +func expandOrgPolicyPolicySpecRules(o interface{}) *orgpolicy.PolicySpecRules { + if o == nil { + return orgpolicy.EmptyPolicySpecRules + } + + obj := o.(map[string]interface{}) + return &orgpolicy.PolicySpecRules{ + AllowAll: expandEnumBool(obj["allow_all"].(string)), + Condition: expandOrgPolicyPolicySpecRulesCondition(obj["condition"]), + DenyAll: expandEnumBool(obj["deny_all"].(string)), + Enforce: expandEnumBool(obj["enforce"].(string)), + Values: expandOrgPolicyPolicySpecRulesValues(obj["values"]), + } +} + +func flattenOrgPolicyPolicySpecRulesArray(objs []orgpolicy.PolicySpecRules) []interface{} { + if objs == nil { + return nil + } + + items := []interface{}{} + for _, item := range objs { + i := flattenOrgPolicyPolicySpecRules(&item) + items = append(items, i) + } + + return items +} + +func flattenOrgPolicyPolicySpecRules(obj *orgpolicy.PolicySpecRules) interface{} { + if obj == nil || obj.Empty() { + return nil + } + transformed := map[string]interface{}{ + "allow_all": flattenEnumBool(obj.AllowAll), + "condition": flattenOrgPolicyPolicySpecRulesCondition(obj.Condition), + "deny_all": flattenEnumBool(obj.DenyAll), + "enforce": flattenEnumBool(obj.Enforce), + "values": flattenOrgPolicyPolicySpecRulesValues(obj.Values), + } + + return transformed + +} + +func expandOrgPolicyPolicySpecRulesCondition(o interface{}) *orgpolicy.PolicySpecRulesCondition { + if o == nil { + return orgpolicy.EmptyPolicySpecRulesCondition + } + objArr := o.([]interface{}) + if len(objArr) == 0 { + return orgpolicy.EmptyPolicySpecRulesCondition + } + obj := objArr[0].(map[string]interface{}) + return &orgpolicy.PolicySpecRulesCondition{ + Description: dcl.String(obj["description"].(string)), + Expression: dcl.String(obj["expression"].(string)), + Location: dcl.String(obj["location"].(string)), + Title: dcl.String(obj["title"].(string)), + } +} + +func flattenOrgPolicyPolicySpecRulesCondition(obj *orgpolicy.PolicySpecRulesCondition) interface{} { + if obj == nil || obj.Empty() { + return nil + } + transformed := map[string]interface{}{ + "description": obj.Description, + "expression": obj.Expression, + "location": obj.Location, + "title": obj.Title, + } + + return []interface{}{transformed} + +} + +func expandOrgPolicyPolicySpecRulesValues(o interface{}) *orgpolicy.PolicySpecRulesValues { + if o == nil { + return orgpolicy.EmptyPolicySpecRulesValues + } + objArr := o.([]interface{}) + if len(objArr) == 0 { + return orgpolicy.EmptyPolicySpecRulesValues + } + obj := objArr[0].(map[string]interface{}) + return &orgpolicy.PolicySpecRulesValues{ + AllowedValues: expandStringArray(obj["allowed_values"]), + DeniedValues: expandStringArray(obj["denied_values"]), + } +} + +func flattenOrgPolicyPolicySpecRulesValues(obj *orgpolicy.PolicySpecRulesValues) interface{} { + if obj == nil || obj.Empty() { + return nil + } + transformed := map[string]interface{}{ + "allowed_values": obj.AllowedValues, + "denied_values": obj.DeniedValues, + } + + return []interface{}{transformed} + +} diff --git a/google/resource_org_policy_policy_generated_test.go b/google/resource_org_policy_policy_generated_test.go new file mode 100644 index 00000000000..26dffb606f2 --- /dev/null +++ b/google/resource_org_policy_policy_generated_test.go @@ -0,0 +1,376 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: DCL *** +// +// ---------------------------------------------------------------------------- +// +// This file is managed by Magic Modules (https://github.com/GoogleCloudPlatform/magic-modules) +// and is based on the DCL (https://github.com/GoogleCloudPlatform/declarative-resource-client-library). +// Changes will need to be made to the DCL or Magic Modules instead of here. +// +// We are not currently able to accept contributions to this file. If changes +// are required, please file an issue at https://github.com/hashicorp/terraform-provider-google/issues/new/choose +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "fmt" + dcl "github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl" + orgpolicy "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/orgpolicy" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "strings" + "testing" +) + +func TestAccOrgPolicyPolicy_EnforcePolicy(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOrgPolicyPolicyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccOrgPolicyPolicy_EnforcePolicy(context), + }, + { + ResourceName: "google_org_policy_policy.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"}, + }, + }, + }) +} +func TestAccOrgPolicyPolicy_FolderPolicy(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOrgPolicyPolicyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccOrgPolicyPolicy_FolderPolicy(context), + }, + { + ResourceName: "google_org_policy_policy.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"}, + }, + { + Config: testAccOrgPolicyPolicy_FolderPolicyUpdate0(context), + }, + { + ResourceName: "google_org_policy_policy.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"}, + }, + }, + }) +} +func TestAccOrgPolicyPolicy_OrganizationPolicy(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOrgPolicyPolicyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccOrgPolicyPolicy_OrganizationPolicy(context), + }, + { + ResourceName: "google_org_policy_policy.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"}, + }, + { + Config: testAccOrgPolicyPolicy_OrganizationPolicyUpdate0(context), + }, + { + ResourceName: "google_org_policy_policy.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"}, + }, + }, + }) +} +func TestAccOrgPolicyPolicy_ProjectPolicy(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckOrgPolicyPolicyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccOrgPolicyPolicy_ProjectPolicy(context), + }, + { + ResourceName: "google_org_policy_policy.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"}, + }, + { + Config: testAccOrgPolicyPolicy_ProjectPolicyUpdate0(context), + }, + { + ResourceName: "google_org_policy_policy.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "spec.0.rules.0.condition.0.expression"}, + }, + }, + }) +} + +func testAccOrgPolicyPolicy_EnforcePolicy(context map[string]interface{}) string { + return Nprintf(` +resource "google_org_policy_policy" "primary" { + name = "projects/${google_project.basic.name}/policies/iam.disableServiceAccountKeyUpload" + parent = "projects/${google_project.basic.name}" + + spec { + rules { + enforce = "FALSE" + } + } +} + +resource "google_project" "basic" { + name = "tf-test-id-test%{random_suffix}" + parent = "organizations/%{org_id}" +} + + +`, context) +} + +func testAccOrgPolicyPolicy_FolderPolicy(context map[string]interface{}) string { + return Nprintf(` +resource "google_org_policy_policy" "primary" { + name = "${google_folder.basic.name}/policies/gcp.resourceLocations" + parent = google_folder.basic.name + + spec { + inherit_from_parent = true + + rules { + deny_all = "TRUE" + } + } +} + +resource "google_folder" "basic" { + parent = "organizations/%{org_id}" + display_name = "folder%{random_suffix}" +} + + +`, context) +} + +func testAccOrgPolicyPolicy_FolderPolicyUpdate0(context map[string]interface{}) string { + return Nprintf(` +resource "google_org_policy_policy" "primary" { + name = "${google_folder.basic.name}/policies/gcp.resourceLocations" + parent = google_folder.basic.name + + spec { + inherit_from_parent = false + + rules { + condition { + description = "A sample condition for the policy" + expression = "resource.matchLabels('labelKeys/123', 'labelValues/345')" + title = "sample-condition" + } + + values { + allowed_values = ["projects/allowed-project"] + denied_values = ["projects/denied-project"] + } + } + + rules { + allow_all = "TRUE" + } + } +} + +resource "google_folder" "basic" { + parent = "organizations/%{org_id}" + display_name = "folder%{random_suffix}" +} + + +`, context) +} + +func testAccOrgPolicyPolicy_OrganizationPolicy(context map[string]interface{}) string { + return Nprintf(` +resource "google_org_policy_policy" "primary" { + name = "organizations/%{org_id}/policies/gcp.detailedAuditLoggingMode" + parent = "organizations/%{org_id}" + + spec { + reset = true + } +} + + +`, context) +} + +func testAccOrgPolicyPolicy_OrganizationPolicyUpdate0(context map[string]interface{}) string { + return Nprintf(` +resource "google_org_policy_policy" "primary" { + name = "organizations/%{org_id}/policies/gcp.detailedAuditLoggingMode" + parent = "organizations/%{org_id}" + + spec { + reset = false + + rules { + enforce = "TRUE" + } + } +} + + +`, context) +} + +func testAccOrgPolicyPolicy_ProjectPolicy(context map[string]interface{}) string { + return Nprintf(` +resource "google_org_policy_policy" "primary" { + name = "projects/${google_project.basic.name}/policies/gcp.resourceLocations" + parent = "projects/${google_project.basic.name}" + + spec { + rules { + condition { + description = "A sample condition for the policy" + expression = "resource.matchLabels('labelKeys/123', 'labelValues/345')" + location = "sample-location.log" + title = "sample-condition" + } + + values { + allowed_values = ["projects/allowed-project"] + denied_values = ["projects/denied-project"] + } + } + + rules { + allow_all = "TRUE" + } + } +} + +resource "google_project" "basic" { + name = "tf-test-id-test%{random_suffix}" + parent = "organizations/%{org_id}" +} + + +`, context) +} + +func testAccOrgPolicyPolicy_ProjectPolicyUpdate0(context map[string]interface{}) string { + return Nprintf(` +resource "google_org_policy_policy" "primary" { + name = "projects/${google_project.basic.name}/policies/gcp.resourceLocations" + parent = "projects/${google_project.basic.name}" + + spec { + rules { + condition { + description = "A new sample condition for the policy" + expression = "false" + location = "new-sample-location.log" + title = "new-sample-condition" + } + + values { + allowed_values = ["projects/new-allowed-project"] + denied_values = ["projects/new-denied-project"] + } + } + + rules { + deny_all = "TRUE" + } + } +} + +resource "google_project" "basic" { + name = "tf-test-id-test%{random_suffix}" + parent = "organizations/%{org_id}" +} + + +`, context) +} + +func testAccCheckOrgPolicyPolicyDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "rs.google_org_policy_policy" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + billingProject := "" + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + obj := &orgpolicy.Policy{ + Name: dcl.String(rs.Primary.Attributes["name"]), + Parent: dcl.String(rs.Primary.Attributes["parent"]), + } + + client := NewDCLOrgPolicyClient(config, config.userAgent, billingProject) + _, err := client.GetPolicy(context.Background(), obj) + if err == nil { + return fmt.Errorf("google_org_policy_policy still exists %v", obj) + } + } + return nil + } +} diff --git a/google/resource_org_policy_policy_sweeper_test.go b/google/resource_org_policy_policy_sweeper_test.go new file mode 100644 index 00000000000..2110702f3dd --- /dev/null +++ b/google/resource_org_policy_policy_sweeper_test.go @@ -0,0 +1,72 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: DCL *** +// +// ---------------------------------------------------------------------------- +// +// This file is managed by Magic Modules (https://github.com/GoogleCloudPlatform/magic-modules) +// and is based on the DCL (https://github.com/GoogleCloudPlatform/declarative-resource-client-library). +// Changes will need to be made to the DCL or Magic Modules instead of here. +// +// We are not currently able to accept contributions to this file. If changes +// are required, please file an issue at https://github.com/hashicorp/terraform-provider-google/issues/new/choose +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "testing" + + orgpolicy "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/orgpolicy" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("OrgPolicyPolicy", &resource.Sweeper{ + Name: "OrgPolicyPolicy", + F: testSweepOrgPolicyPolicy, + }) +} + +func testSweepOrgPolicyPolicy(region string) error { + resourceName := "OrgPolicyPolicy" + 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 be used for Delete arguments. + d := map[string]string{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + } + + client := NewDCLOrgPolicyClient(config, config.userAgent, "") + err = client.DeleteAllPolicy(context.Background(), d["parent"], isDeletableOrgPolicyPolicy) + if err != nil { + return err + } + return nil +} + +func isDeletableOrgPolicyPolicy(r *orgpolicy.Policy) bool { + return isSweepableTestResource(*r.Name) +} diff --git a/google/resource_privateca_certificate_template.go b/google/resource_privateca_certificate_template.go index b01a9a0546e..d1d275711c1 100644 --- a/google/resource_privateca_certificate_template.go +++ b/google/resource_privateca_certificate_template.go @@ -486,7 +486,7 @@ func resourcePrivatecaCertificateTemplateCreate(d *schema.ResourceData, meta int id, err := replaceVarsForId(d, config, "projects/{{project}}/locations/{{location}}/certificateTemplates/{{name}}") if err != nil { - return fmt.Errorf("Error constructing id: %s", err) + return fmt.Errorf("error constructing id: %s", err) } d.SetId(id) createDirective := CreateDirective diff --git a/website/docs/r/org_policy_policy.html.markdown b/website/docs/r/org_policy_policy.html.markdown new file mode 100644 index 00000000000..516efffda42 --- /dev/null +++ b/website/docs/r/org_policy_policy.html.markdown @@ -0,0 +1,240 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: DCL *** +# +# ---------------------------------------------------------------------------- +# +# This file is managed by Magic Modules (https:#github.com/GoogleCloudPlatform/magic-modules) +# and is based on the DCL (https:#github.com/GoogleCloudPlatform/declarative-resource-client-library). +# Changes will need to be made to the DCL or Magic Modules instead of here. +# +# We are not currently able to accept contributions to this file. If changes +# are required, please file an issue at https:#github.com/hashicorp/terraform-provider-google/issues/new/choose +# +# ---------------------------------------------------------------------------- +subcategory: "OrgPolicy" +layout: "google" +page_title: "Google: google_org_policy_policy" +sidebar_current: "docs-google-org-policy-policy" +description: |- + +--- + +# google\_org\_policy\_policy + + +## Example Usage - enforce_policy +A test of an enforce orgpolicy policy for a project +```hcl +resource "google_org_policy_policy" "primary" { + name = "projects/${google_project.basic.name}/policies/iam.disableServiceAccountKeyUpload" + parent = "projects/${google_project.basic.name}" + + spec { + rules { + enforce = "FALSE" + } + } +} + +resource "google_project" "basic" { + project_id = "id-test" + name = "id-test" + org_id = "123456789" +} + + +``` +## Example Usage - folder_policy +A test of an orgpolicy policy for a folder +```hcl +resource "google_org_policy_policy" "primary" { + name = "${google_folder.basic.name}/policies/gcp.resourceLocations" + parent = google_folder.basic.name + + spec { + inherit_from_parent = true + + rules { + deny_all = "TRUE" + } + } +} + +resource "google_folder" "basic" { + parent = "organizations/123456789" + display_name = "folder" +} + + +``` +## Example Usage - organization_policy +A test of an orgpolicy policy for an organization +```hcl +resource "google_org_policy_policy" "primary" { + name = "organizations/123456789/policies/gcp.detailedAuditLoggingMode" + parent = "organizations/123456789" + + spec { + reset = true + } +} + + +``` +## Example Usage - project_policy +A test of an orgpolicy policy for a project +```hcl +resource "google_org_policy_policy" "primary" { + name = "projects/${google_project.basic.name}/policies/gcp.resourceLocations" + parent = "projects/${google_project.basic.name}" + + spec { + rules { + condition { + description = "A sample condition for the policy" + expression = "resource.matchLabels('labelKeys/123', 'labelValues/345')" + location = "sample-location.log" + title = "sample-condition" + } + + values { + allowed_values = ["projects/allowed-project"] + denied_values = ["projects/denied-project"] + } + } + + rules { + allow_all = "TRUE" + } + } +} + +resource "google_project" "basic" { + project_id = "id-test" + name = "id-test" + org_id = "123456789" +} + + +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - + (Required) + Immutable. The resource name of the Policy. Must be one of the following forms, where constraint_name is the name of the constraint which this Policy configures: * `projects/{project_number}/policies/{constraint_name}` * `folders/{folder_id}/policies/{constraint_name}` * `organizations/{organization_id}/policies/{constraint_name}` For example, "projects/123/policies/compute.disableSerialPortAccess". Note: `projects/{project_id}/policies/{constraint_name}` is also an acceptable name for API requests, but responses will return the name using the equivalent project number. + +* `parent` - + (Required) + The parent of the resource. + + + +- - - + +* `spec` - + (Optional) + Basic information about the Organization Policy. + + + +The `spec` block supports: + +* `etag` - + An opaque tag indicating the current version of the `Policy`, used for concurrency control. This field is ignored if used in a `CreatePolicy` request. When the `Policy` is returned from either a `GetPolicy` or a `ListPolicies` request, this `etag` indicates the version of the current `Policy` to use when executing a read-modify-write loop. When the `Policy` is returned from a `GetEffectivePolicy` request, the `etag` will be unset. + +* `inherit_from_parent` - + (Optional) + Determines the inheritance behavior for this `Policy`. If `inherit_from_parent` is true, PolicyRules set higher up in the hierarchy (up to the closest root) are inherited and present in the effective policy. If it is false, then no rules are inherited, and this Policy becomes the new root for evaluation. This field can be set only for Policies which configure list constraints. + +* `reset` - + (Optional) + Ignores policies set above this resource and restores the `constraint_default` enforcement behavior of the specific `Constraint` at this resource. This field can be set in policies for either list or boolean constraints. If set, `rules` must be empty and `inherit_from_parent` must be set to false. + +* `rules` - + (Optional) + Up to 10 PolicyRules are allowed. In Policies for boolean constraints, the following requirements apply: - There must be one and only one PolicyRule where condition is unset. - BooleanPolicyRules with conditions must set `enforced` to the opposite of the PolicyRule without a condition. - During policy evaluation, PolicyRules with conditions that are true for a target resource take precedence. + +* `update_time` - + Output only. The time stamp this was previously updated. This represents the last time a call to `CreatePolicy` or `UpdatePolicy` was made for that `Policy`. + +The `rules` block supports: + +* `allow_all` - + (Optional) + Setting this to true means that all values are allowed. This field can be set only in Policies for list constraints. + +* `condition` - + (Optional) + A condition which determines whether this rule is used in the evaluation of the policy. When set, the `expression` field in the `Expr' must include from 1 to 10 subexpressions, joined by the "||" or "&&" operators. Each subexpression must be of the form "resource.matchTag('/tag_key_short_name, 'tag_value_short_name')". or "resource.matchTagId('tagKeys/key_id', 'tagValues/value_id')". where key_name and value_name are the resource names for Label Keys and Values. These names are available from the Tag Manager Service. An example expression is: "resource.matchTag('123456789/environment, 'prod')". or "resource.matchTagId('tagKeys/123', 'tagValues/456')". + +* `deny_all` - + (Optional) + Setting this to true means that all values are denied. This field can be set only in Policies for list constraints. + +* `enforce` - + (Optional) + If `true`, then the `Policy` is enforced. If `false`, then any configuration is acceptable. This field can be set only in Policies for boolean constraints. + +* `values` - + (Optional) + List of values to be used for this PolicyRule. This field can be set only in Policies for list constraints. + +The `condition` block supports: + +* `description` - + (Optional) + Optional. Description of the expression. This is a longer text which describes the expression, e.g. when hovered over it in a UI. + +* `expression` - + (Optional) + Textual representation of an expression in Common Expression Language syntax. + +* `location` - + (Optional) + Optional. String indicating the location of the expression for error reporting, e.g. a file name and a position in the file. + +* `title` - + (Optional) + Optional. Title for the expression, i.e. a short string describing its purpose. This can be used e.g. in UIs which allow to enter the expression. + +The `values` block supports: + +* `allowed_values` - + (Optional) + List of values allowed at this resource. + +* `denied_values` - + (Optional) + List of values denied at this resource. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `{{parent}}/policies/{{name}}` + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 10 minutes. +- `update` - Default is 10 minutes. +- `delete` - Default is 10 minutes. + +## Import + +Policy can be imported using any of these accepted formats: + +``` +$ terraform import google_org_policy_policy.default {{parent}}/policies/{{name}} +$ terraform import google_org_policy_policy.default {{parent}}/{{name}} +``` + + + diff --git a/website/google.erb b/website/google.erb index e4e613c2373..736f0378171 100644 --- a/website/google.erb +++ b/website/google.erb @@ -2807,6 +2807,22 @@ +
  • + OrgPolicy + +
  • +
  • Privateca