Add new resource google_folder_organization_policy (#747)

* Add new resource google_folder_organization_policy
* Add documentation
This commit is contained in:
Vincent Roseberry 2017-11-27 14:53:01 -08:00 committed by GitHub
parent c69fc07a6d
commit 10f764aa9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 629 additions and 97 deletions

View File

@ -118,6 +118,7 @@ func Provider() terraform.ResourceProvider {
"google_dns_record_set": resourceDnsRecordSet(),
"google_folder": resourceGoogleFolder(),
"google_folder_iam_policy": ResourceIamPolicy(IamFolderSchema, NewFolderIamUpdater),
"google_folder_organization_policy": resourceGoogleFolderOrganizationPolicy(),
"google_logging_billing_account_sink": resourceLoggingBillingAccountSink(),
"google_logging_folder_sink": resourceLoggingFolderSink(),
"google_logging_project_sink": resourceLoggingProjectSink(),

View File

@ -0,0 +1,101 @@
package google
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudresourcemanager/v1"
)
func resourceGoogleFolderOrganizationPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceGoogleFolderOrganizationPolicyCreate,
Read: resourceGoogleFolderOrganizationPolicyRead,
Update: resourceGoogleFolderOrganizationPolicyUpdate,
Delete: resourceGoogleFolderOrganizationPolicyDelete,
Schema: mergeSchemas(
schemaOrganizationPolicy,
map[string]*schema.Schema{
"folder": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
),
}
}
func resourceGoogleFolderOrganizationPolicyCreate(d *schema.ResourceData, meta interface{}) error {
if err := setFolderOrganizationPolicy(d, meta); err != nil {
return err
}
d.SetId(fmt.Sprintf("%s:%s", d.Get("folder"), d.Get("constraint")))
return resourceGoogleFolderOrganizationPolicyRead(d, meta)
}
func resourceGoogleFolderOrganizationPolicyRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
folder := d.Get("folder").(string)
policy, err := config.clientResourceManager.Folders.GetOrgPolicy(folder, &cloudresourcemanager.GetOrgPolicyRequest{
Constraint: canonicalOrgPolicyConstraint(d.Get("constraint").(string)),
}).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Organization policy for %s", folder))
}
d.Set("constraint", policy.Constraint)
d.Set("boolean_policy", flattenBooleanOrganizationPolicy(policy.BooleanPolicy))
d.Set("list_policy", flattenListOrganizationPolicy(policy.ListPolicy))
d.Set("version", policy.Version)
d.Set("etag", policy.Etag)
d.Set("update_time", policy.UpdateTime)
return nil
}
func resourceGoogleFolderOrganizationPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
if err := setFolderOrganizationPolicy(d, meta); err != nil {
return err
}
return resourceGoogleFolderOrganizationPolicyRead(d, meta)
}
func resourceGoogleFolderOrganizationPolicyDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
_, err := config.clientResourceManager.Folders.ClearOrgPolicy(d.Get("folder").(string), &cloudresourcemanager.ClearOrgPolicyRequest{
Constraint: canonicalOrgPolicyConstraint(d.Get("constraint").(string)),
}).Do()
if err != nil {
return err
}
return nil
}
func setFolderOrganizationPolicy(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
listPolicy, err := expandListOrganizationPolicy(d.Get("list_policy").([]interface{}))
if err != nil {
return err
}
_, err = config.clientResourceManager.Folders.SetOrgPolicy(d.Get("folder").(string), &cloudresourcemanager.SetOrgPolicyRequest{
Policy: &cloudresourcemanager.OrgPolicy{
Constraint: canonicalOrgPolicyConstraint(d.Get("constraint").(string)),
BooleanPolicy: expandBooleanOrganizationPolicy(d.Get("boolean_policy").([]interface{})),
ListPolicy: listPolicy,
Version: int64(d.Get("version").(int)),
Etag: d.Get("etag").(string),
},
}).Do()
return err
}

View File

@ -0,0 +1,316 @@
package google
import (
"fmt"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"google.golang.org/api/cloudresourcemanager/v1"
"reflect"
"testing"
)
func TestAccGoogleFolderOrganizationPolicy_boolean(t *testing.T) {
t.Parallel()
folder := acctest.RandomWithPrefix("tf-test")
org := getTestOrgFromEnv(t)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleFolderOrganizationPolicyDestroy,
Steps: []resource.TestStep{
{
// Test creation of an enforced boolean policy
Config: testAccGoogleFolderOrganizationPolicy_boolean(org, folder, true),
Check: testAccCheckGoogleFolderOrganizationBooleanPolicy("bool", true),
},
{
// Test update from enforced to not
Config: testAccGoogleFolderOrganizationPolicy_boolean(org, folder, false),
Check: testAccCheckGoogleFolderOrganizationBooleanPolicy("bool", false),
},
{
Config: " ",
Destroy: true,
},
{
// Test creation of a not enforced boolean policy
Config: testAccGoogleFolderOrganizationPolicy_boolean(org, folder, false),
Check: testAccCheckGoogleFolderOrganizationBooleanPolicy("bool", false),
},
{
// Test update from not enforced to enforced
Config: testAccGoogleFolderOrganizationPolicy_boolean(org, folder, true),
Check: testAccCheckGoogleFolderOrganizationBooleanPolicy("bool", true),
},
},
})
}
func TestAccGoogleFolderOrganizationPolicy_list_allowAll(t *testing.T) {
t.Parallel()
folder := acctest.RandomWithPrefix("tf-test")
org := getTestOrgFromEnv(t)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleFolderOrganizationPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccGoogleFolderOrganizationPolicy_list_allowAll(org, folder),
Check: testAccCheckGoogleFolderOrganizationListPolicyAll("list", "ALLOW"),
},
},
})
}
func TestAccGoogleFolderOrganizationPolicy_list_allowSome(t *testing.T) {
t.Parallel()
folder := acctest.RandomWithPrefix("tf-test")
org := getTestOrgFromEnv(t)
project := getTestProjectFromEnv()
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleFolderOrganizationPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccGoogleFolderOrganizationPolicy_list_allowSome(org, folder, project),
Check: testAccCheckGoogleFolderOrganizationListPolicyAllowedValues("list", []string{project}),
},
},
})
}
func TestAccGoogleFolderOrganizationPolicy_list_denySome(t *testing.T) {
t.Parallel()
folder := acctest.RandomWithPrefix("tf-test")
org := getTestOrgFromEnv(t)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleFolderOrganizationPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccGoogleFolderOrganizationPolicy_list_denySome(org, folder),
Check: testAccCheckGoogleFolderOrganizationListPolicyDeniedValues("list", DENIED_ORG_POLICIES),
},
},
})
}
func TestAccGoogleFolderOrganizationPolicy_list_update(t *testing.T) {
t.Parallel()
folder := acctest.RandomWithPrefix("tf-test")
org := getTestOrgFromEnv(t)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleFolderOrganizationPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccGoogleFolderOrganizationPolicy_list_allowAll(org, folder),
Check: testAccCheckGoogleFolderOrganizationListPolicyAll("list", "ALLOW"),
},
{
Config: testAccGoogleFolderOrganizationPolicy_list_denySome(org, folder),
Check: testAccCheckGoogleFolderOrganizationListPolicyDeniedValues("list", DENIED_ORG_POLICIES),
},
},
})
}
func testAccCheckGoogleFolderOrganizationPolicyDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_folder_organization_policy" {
continue
}
folder := rs.Primary.Attributes["folder"]
constraint := canonicalOrgPolicyConstraint(rs.Primary.Attributes["constraint"])
policy, err := config.clientResourceManager.Folders.GetOrgPolicy(folder, &cloudresourcemanager.GetOrgPolicyRequest{
Constraint: constraint,
}).Do()
if err != nil {
return err
}
if policy.ListPolicy != nil || policy.BooleanPolicy != nil {
return fmt.Errorf("Org policy with constraint '%s' hasn't been cleared", constraint)
}
}
return nil
}
func testAccCheckGoogleFolderOrganizationBooleanPolicy(n string, enforced bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
policy, err := getGoogleFolderOrganizationPolicyTestResource(s, n)
if err != nil {
return err
}
if policy.BooleanPolicy.Enforced != enforced {
return fmt.Errorf("Expected boolean policy enforcement to be '%t', got '%t'", enforced, policy.BooleanPolicy.Enforced)
}
return nil
}
}
func testAccCheckGoogleFolderOrganizationListPolicyAll(n, policyType string) resource.TestCheckFunc {
return func(s *terraform.State) error {
policy, err := getGoogleFolderOrganizationPolicyTestResource(s, n)
if err != nil {
return err
}
if len(policy.ListPolicy.AllowedValues) > 0 || len(policy.ListPolicy.DeniedValues) > 0 {
return fmt.Errorf("The `values` field shouldn't be set")
}
if policy.ListPolicy.AllValues != policyType {
return fmt.Errorf("The list policy should %s all values", policyType)
}
return nil
}
}
func testAccCheckGoogleFolderOrganizationListPolicyAllowedValues(n string, values []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
policy, err := getGoogleFolderOrganizationPolicyTestResource(s, n)
if err != nil {
return err
}
if !reflect.DeepEqual(policy.ListPolicy.AllowedValues, values) {
return fmt.Errorf("Expected the list policy to allow '%s', instead allowed '%s'", values, policy.ListPolicy.AllowedValues)
}
return nil
}
}
func testAccCheckGoogleFolderOrganizationListPolicyDeniedValues(n string, values []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
policy, err := getGoogleFolderOrganizationPolicyTestResource(s, n)
if err != nil {
return err
}
if !reflect.DeepEqual(policy.ListPolicy.DeniedValues, values) {
return fmt.Errorf("Expected the list policy to deny '%s', instead denied '%s'", values, policy.ListPolicy.DeniedValues)
}
return nil
}
}
func getGoogleFolderOrganizationPolicyTestResource(s *terraform.State, n string) (*cloudresourcemanager.OrgPolicy, error) {
rn := "google_folder_organization_policy." + n
rs, ok := s.RootModule().Resources[rn]
if !ok {
return nil, fmt.Errorf("Not found: %s", rn)
}
if rs.Primary.ID == "" {
return nil, fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
return config.clientResourceManager.Folders.GetOrgPolicy(rs.Primary.Attributes["folder"], &cloudresourcemanager.GetOrgPolicyRequest{
Constraint: rs.Primary.Attributes["constraint"],
}).Do()
}
func testAccGoogleFolderOrganizationPolicy_boolean(org, folder string, enforced bool) string {
return fmt.Sprintf(`
resource "google_folder" "orgpolicy" {
display_name = "%s"
parent = "%s"
}
resource "google_folder_organization_policy" "bool" {
folder = "${google_folder.orgpolicy.name}"
constraint = "constraints/compute.disableSerialPortAccess"
boolean_policy {
enforced = %t
}
}
`, folder, "organizations/"+org, enforced)
}
func testAccGoogleFolderOrganizationPolicy_list_allowAll(org, folder string) string {
return fmt.Sprintf(`
resource "google_folder" "orgpolicy" {
display_name = "%s"
parent = "%s"
}
resource "google_folder_organization_policy" "list" {
folder = "${google_folder.orgpolicy.name}"
constraint = "constraints/serviceuser.services"
list_policy {
allow {
all = true
}
}
}
`, folder, "organizations/"+org)
}
func testAccGoogleFolderOrganizationPolicy_list_allowSome(org, folder, project string) string {
return fmt.Sprintf(`
resource "google_folder" "orgpolicy" {
display_name = "%s"
parent = "%s"
}
resource "google_folder_organization_policy" "list" {
folder = "${google_folder.orgpolicy.name}"
constraint = "constraints/compute.trustedImageProjects"
list_policy {
allow {
values = ["%s"]
}
}
}
`, folder, "organizations/"+org, project)
}
func testAccGoogleFolderOrganizationPolicy_list_denySome(org, folder string) string {
return fmt.Sprintf(`
resource "google_folder" "orgpolicy" {
display_name = "%s"
parent = "%s"
}
resource "google_folder_organization_policy" "list" {
folder = "${google_folder.orgpolicy.name}"
constraint = "serviceuser.services"
list_policy {
deny {
values = [
"maps-ios-backend.googleapis.com",
"placesios.googleapis.com",
]
}
}
}
`, folder, "organizations/"+org)
}

View File

@ -7,6 +7,100 @@ import (
"strings"
)
var schemaOrganizationPolicy = map[string]*schema.Schema{
"constraint": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: linkDiffSuppress,
},
"boolean_policy": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"list_policy"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enforced": {
Type: schema.TypeBool,
Required: true,
},
},
},
},
"list_policy": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"boolean_policy"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allow": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"list_policy.0.deny"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"all": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{"list_policy.0.allow.0.values"},
},
"values": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"deny": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"all": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{"list_policy.0.deny.0.values"},
},
"values": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"suggested_value": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
},
},
"version": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
"update_time": {
Type: schema.TypeString,
Computed: true,
},
}
func resourceGoogleOrganizationPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceGoogleOrganizationPolicyCreate,
@ -18,104 +112,15 @@ func resourceGoogleOrganizationPolicy() *schema.Resource {
State: resourceGoogleOrganizationPolicyImportState,
},
Schema: map[string]*schema.Schema{
"org_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"constraint": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: linkDiffSuppress,
},
"boolean_policy": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"list_policy"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enforced": {
Type: schema.TypeBool,
Required: true,
},
},
Schema: mergeSchemas(
schemaOrganizationPolicy,
map[string]*schema.Schema{
"org_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
"list_policy": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"boolean_policy"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allow": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
ConflictsWith: []string{"list_policy.0.deny"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"all": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{"list_policy.0.allow.0.values"},
},
"values": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"deny": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"all": {
Type: schema.TypeBool,
Optional: true,
Default: false,
ConflictsWith: []string{"list_policy.0.deny.0.values"},
},
"values": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},
"suggested_value": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
},
},
"version": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"etag": {
Type: schema.TypeString,
Computed: true,
},
"update_time": {
Type: schema.TypeString,
Computed: true,
},
},
}),
}
}

View File

@ -0,0 +1,106 @@
---
layout: "google"
page_title: "Google: google_folder_organization_policy"
sidebar_current: "docs-google-folder-organization-policy"
description: |-
Allows management of Organization policies for a Google Folder.
---
# google\_folder\_organization\_policy
Allows management of Organization policies for a Google Folder. For more information see
[the official
documentation](https://cloud.google.com/resource-manager/docs/organization-policy/overview) and
[API](https://cloud.google.com/resource-manager/reference/rest/v1/folders/setOrgPolicy).
## Example Usage
To set policy with a [boolean constraint](https://cloud.google.com/resource-manager/docs/organization-policy/quickstart-boolean-constraints):
```hcl
resource "google_folder_organization_policy" "serial_port_policy" {
org_id = "123456789"
constraint = "compute.disableSerialPortAccess"
boolean_policy {
enforced = true
}
}
```
To set a policy with a [list contraint](https://cloud.google.com/resource-manager/docs/organization-policy/quickstart-list-constraints):
```hcl
resource "google_folder_organization_policy" "services_policy" {
org_id = "123456789"
constraint = "serviceuser.services"
list_policy {
allow {
all = true
}
}
}
```
Or to deny some services, use the following instead:
```hcl
resource "google_folder_organization_policy" "services_policy" {
org_id = "123456789"
constraint = "serviceuser.services"
list_policy {
suggested_values = "compute.googleapis.com"
deny {
values = ["cloudresourcemanager.googleapis.com"]
}
}
}
```
## Argument Reference
The following arguments are supported:
* `folder` - (Required) The numeric ID of the organization to set the policy for.
* `constraint` - (Required) The name of the Constraint the Policy is configuring, for example, `serviceuser.services`. Check out the [complete list of available constraints](https://cloud.google.com/resource-manager/docs/organization-policy/understanding-constraints#available_constraints).
- - -
* `version` - (Optional) Version of the Policy. Default version is 0.
* `boolean_policy` - (Optional) A boolean policy is a constraint that is either enforced or not. Structure is documented below.
* `list_policy` - (Optional) A policy that can define specific values that are allowed or denied for the given constraint. It can also be used to allow or deny all values. Structure is documented below.
- - -
The `boolean_policy` block supports:
* `enforced` - (Required) If true, then the Policy is enforced. If false, then any configuration is acceptable.
The `list_policy` block supports:
* `allow` or `deny` - (Optional) One or the other must be set.
* `suggested_values` - (Optional) The Google Cloud Console will try to default to a configuration that matches the value specified in this field.
The `allow` or `deny` blocks support:
* `all` - (Optional) The policy allows or denies all values.
* `values` - (Optional) The policy can define specific values that are allowed or denied.
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are
exported:
* `etag` - (Computed) The etag of the organization policy. `etag` is used for optimistic concurrency control as a way to help prevent simultaneous updates of a policy from overwriting each other.
* `update_time` - (Computed) The timestamp in RFC3339 UTC "Zulu" format, accurate to nanoseconds, representing when the variable was last updated. Example: "2016-10-09T12:33:37.578138407Z".

View File

@ -86,6 +86,9 @@
<li<%= sidebar_current("docs-google-folder-iam-policy") %>>
<a href="/docs/providers/google/r/google_folder_iam_policy.html">google_folder_iam_policy</a>
</li>
<li<%= sidebar_current("docs-google-folder-organization-policy") %>>
<a href="/docs/providers/google/r/google_folder_organization_policy.html">google_organization_iam_policy</a>
</li>
<li<%= sidebar_current("docs-google-kms-key-ring") %>>
<a href="/docs/providers/google/r/google_kms_key_ring.html">google_kms_key_ring</a>
</li>