diff --git a/build/terraform b/build/terraform index 13309a73cb1f..f8afcfe9733f 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit 13309a73cb1f6f4d5ea35d42cec731a25789dd35 +Subproject commit f8afcfe9733f3054a4c89520f2cb2a80f604b9a9 diff --git a/build/terraform-beta b/build/terraform-beta index e6227d29e2d2..9abb5101c794 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit e6227d29e2d23dd33c17e8e26904c494c7f5ad9c +Subproject commit 9abb5101c794c05757faa3e08fe2fca0b4107504 diff --git a/third_party/terraform/resources/resource_google_project_iam_policy.go b/third_party/terraform/resources/resource_google_project_iam_policy.go.erb similarity index 94% rename from third_party/terraform/resources/resource_google_project_iam_policy.go rename to third_party/terraform/resources/resource_google_project_iam_policy.go.erb index bc780e7f639f..c2c48191adca 100644 --- a/third_party/terraform/resources/resource_google_project_iam_policy.go +++ b/third_party/terraform/resources/resource_google_project_iam_policy.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -150,6 +151,10 @@ func resourceGoogleProjectIamPolicyImport(d *schema.ResourceData, meta interface } func setProjectIamPolicy(policy *cloudresourcemanager.Policy, config *Config, pid string) error { +<% unless version == 'ga' -%> + policy.Version = iamPolicyVersion + +<% end -%> // Apply the policy pbytes, _ := json.Marshal(policy) log.Printf("[DEBUG] Setting policy %#v for project: %s", string(pbytes), pid) @@ -175,8 +180,17 @@ func getResourceIamPolicy(d *schema.ResourceData) (*cloudresourcemanager.Policy, // Retrieve the existing IAM Policy for a Project func getProjectIamPolicy(project string, config *Config) (*cloudresourcemanager.Policy, error) { +<% if version == 'ga' -%> p, err := config.clientResourceManager.Projects.GetIamPolicy(project, &cloudresourcemanager.GetIamPolicyRequest{}).Do() +<% else -%> + p, err := config.clientResourceManager.Projects.GetIamPolicy(project, + &cloudresourcemanager.GetIamPolicyRequest{ + Options: &cloudresourcemanager.GetPolicyOptions{ + RequestedPolicyVersion: iamPolicyVersion, + }, + }).Do() +<% end -%> if err != nil { return nil, fmt.Errorf("Error retrieving IAM policy for project %q: %s", project, err) diff --git a/third_party/terraform/tests/resource_google_project_iam_binding_test.go b/third_party/terraform/tests/resource_google_project_iam_binding_test.go.erb similarity index 82% rename from third_party/terraform/tests/resource_google_project_iam_binding_test.go rename to third_party/terraform/tests/resource_google_project_iam_binding_test.go.erb index 660f7a4b64e5..99391948c54a 100644 --- a/third_party/terraform/tests/resource_google_project_iam_binding_test.go +++ b/third_party/terraform/tests/resource_google_project_iam_binding_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -213,6 +214,41 @@ func TestAccProjectIamBinding_noMembers(t *testing.T) { }) } +<% unless version == 'ga' -%> +func TestAccProjectIamBinding_withCondition(t *testing.T) { + t.Skipf("IAM Conditions is not whitelisted for new projects in this org, enable this test once it's public beta") + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + role := "roles/compute.instanceAdmin" + conditionTitle := "expires_after_2019_12_31" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateBinding_withCondition(pid, pname, org, role, conditionTitle), + }, + { + ResourceName: "google_project_iam_binding.acceptance", + ImportStateId: fmt.Sprintf("%s %s %s", pid, role, conditionTitle), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +<% end -%> + func testAccProjectAssociateBindingBasic(pid, name, org, role string) string { return fmt.Sprintf(` resource "google_project" "acceptance" { @@ -298,3 +334,26 @@ resource "google_project_iam_binding" "acceptance" { } `, pid, name, org, role) } + +<% unless version == 'ga' -%> +func testAccProjectAssociateBinding_withCondition(pid, name, org, role, conditionTitle string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_binding" "acceptance" { + project = "${google_project.acceptance.project_id}" + members = ["user:admin@hashicorptest.com"] + role = "%s" + condition { + title = "%s" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +`, pid, name, org, role, conditionTitle) +} +<% end -%> diff --git a/third_party/terraform/tests/resource_google_project_iam_member_test.go b/third_party/terraform/tests/resource_google_project_iam_member_test.go.erb similarity index 72% rename from third_party/terraform/tests/resource_google_project_iam_member_test.go rename to third_party/terraform/tests/resource_google_project_iam_member_test.go.erb index 1480bd3d2542..6271088972e1 100644 --- a/third_party/terraform/tests/resource_google_project_iam_member_test.go +++ b/third_party/terraform/tests/resource_google_project_iam_member_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -130,6 +131,43 @@ func TestAccProjectIamMember_remove(t *testing.T) { }) } +<% unless version == 'ga' -%> +func TestAccProjectIamMember_withCondition(t *testing.T) { + t.Skipf("IAM Conditions is not whitelisted for new projects in this org, enable this test once it's public beta") + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resourceName := "google_project_iam_member.acceptance" + role := "roles/compute.instanceAdmin" + member := "user:admin@hashicorptest.com" + conditionTitle := "expires_after_2019_12_31" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM binding + { + Config: testAccProjectAssociateMember_withCondition(pid, pname, org, role, member, conditionTitle), + }, + { + ResourceName: resourceName, + ImportStateId: fmt.Sprintf("%s %s %s %s", pid, role, member, conditionTitle), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +<% end -%> + func testAccProjectAssociateMemberBasic(pid, name, org, role, member string) string { return fmt.Sprintf(` resource "google_project" "acceptance" { @@ -167,3 +205,26 @@ resource "google_project_iam_member" "multiple" { } `, pid, name, org, role, member, role2, member2) } + +<% unless version == 'ga' -%> +func testAccProjectAssociateMember_withCondition(pid, name, org, role, member, conditionTitle string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_member" "acceptance" { + project = "${google_project.acceptance.project_id}" + role = "%s" + member = "%s" + condition { + title = "%s" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +`, pid, name, org, role, member, conditionTitle) +} +<% end -%> diff --git a/third_party/terraform/tests/resource_google_project_iam_policy_test.go b/third_party/terraform/tests/resource_google_project_iam_policy_test.go.erb similarity index 85% rename from third_party/terraform/tests/resource_google_project_iam_policy_test.go rename to third_party/terraform/tests/resource_google_project_iam_policy_test.go.erb index 89e61b6938e0..124fd7707ea3 100644 --- a/third_party/terraform/tests/resource_google_project_iam_policy_test.go +++ b/third_party/terraform/tests/resource_google_project_iam_policy_test.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -128,6 +129,38 @@ func TestAccProjectIamPolicy_expandedAuditConfig(t *testing.T) { }) } +<% unless version == 'ga' -%> +func TestAccProjectIamPolicy_withCondition(t *testing.T) { + t.Skipf("IAM Conditions is not whitelisted for new projects in this org, enable this test once it's public beta") + t.Parallel() + + org := getTestOrgFromEnv(t) + pid := "terraform-" + acctest.RandString(10) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + // Create a new project + { + Config: testAccProject_create(pid, pname, org), + Check: resource.ComposeTestCheckFunc( + testAccProjectExistingPolicy(pid), + ), + }, + // Apply an IAM policy from a data source. The application + // merges policies, so we validate the expected state. + { + Config: testAccProjectAssociatePolicy_withCondition(pid, pname, org), + }, + { + ResourceName: "google_project_iam_policy.acceptance", + ImportState: true, + }, + }, + }) +} +<% end -%> + func getStatePrimaryResource(s *terraform.State, res, expectedID string) (*terraform.InstanceState, error) { // Get the project resource resource, ok := s.RootModule().Resources[res] @@ -393,3 +426,41 @@ data "google_iam_policy" "expanded" { } }`, pid, name, org) } + +<% unless version == 'ga' -%> +func testAccProjectAssociatePolicy_withCondition(pid, name, org string) string { + return fmt.Sprintf(` +resource "google_project" "acceptance" { + project_id = "%s" + name = "%s" + org_id = "%s" +} + +resource "google_project_iam_policy" "acceptance" { + project = "${google_project.acceptance.id}" + policy_data = "${data.google_iam_policy.admin.policy_data}" +} + +data "google_iam_policy" "admin" { + binding { + role = "roles/storage.objectViewer" + members = [ + "user:evanbrown@google.com", + ] + } + binding { + role = "roles/compute.instanceAdmin" + members = [ + "user:evanbrown@google.com", + "user:evandbrown@gmail.com", + ] + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } + } +} +`, pid, name, org) +} +<% end -%> diff --git a/third_party/terraform/utils/iam_project.go b/third_party/terraform/utils/iam_project.go.erb similarity index 85% rename from third_party/terraform/utils/iam_project.go rename to third_party/terraform/utils/iam_project.go.erb index c2f3cc8549ff..6e2736a5d49b 100644 --- a/third_party/terraform/utils/iam_project.go +++ b/third_party/terraform/utils/iam_project.go.erb @@ -1,3 +1,4 @@ +<% autogen_exception -%> package google import ( @@ -42,8 +43,17 @@ func ProjectIdParseFunc(d *schema.ResourceData, _ *Config) error { } func (u *ProjectIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { +<% if version == 'ga' -%> p, err := u.Config.clientResourceManager.Projects.GetIamPolicy(u.resourceId, &cloudresourcemanager.GetIamPolicyRequest{}).Do() +<% else -%> + p, err := u.Config.clientResourceManager.Projects.GetIamPolicy(u.resourceId, + &cloudresourcemanager.GetIamPolicyRequest{ + Options: &cloudresourcemanager.GetPolicyOptions{ + RequestedPolicyVersion: iamPolicyVersion, + }, + }).Do() +<% end -%> if err != nil { return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) diff --git a/third_party/terraform/website/docs/r/google_project_iam.html.markdown b/third_party/terraform/website/docs/r/google_project_iam.html.markdown index b224fd4e4458..94c95c523f77 100644 --- a/third_party/terraform/website/docs/r/google_project_iam.html.markdown +++ b/third_party/terraform/website/docs/r/google_project_iam.html.markdown @@ -47,6 +47,31 @@ data "google_iam_policy" "admin" { } ``` +With IAM Conditions ([beta](https://terraform.io/docs/providers/google/provider_versions.html), Whitelist-only): + +```hcl +resource "google_project_iam_policy" "project" { + project = "your-project-id" + policy_data = "${data.google_iam_policy.admin.policy_data}" +} + +data "google_iam_policy" "admin" { + binding { + role = "roles/editor" + + members = [ + "user:jane@example.com", + ] + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } + } +} +``` + ## google\_project\_iam\_binding ~> **Note:** If `role` is set to `roles/owner` and you don't specify a user or service account you have access to in `members`, you can lock yourself out of your project. @@ -62,6 +87,25 @@ resource "google_project_iam_binding" "project" { } ``` +With IAM Conditions ([beta](https://terraform.io/docs/providers/google/provider_versions.html), Whitelist-only): + +```hcl +resource "google_project_iam_binding" "project" { + project = "your-project-id" + role = "roles/editor" + + members = [ + "user:jane@example.com", + ] + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +``` + ## google\_project\_iam\_member ```hcl @@ -72,6 +116,22 @@ resource "google_project_iam_member" "project" { } ``` +With IAM Conditions ([beta](https://terraform.io/docs/providers/google/provider_versions.html), Whitelist-only): + +```hcl +resource "google_project_iam_member" "project" { + project = "your-project-id" + role = "roles/editor" + member = "user:jane@example.com" + + condition { + title = "expires_after_2019_12_31" + description = "Expiring at midnight of 2019-12-31" + expression = "request.time < timestamp(\"2020-01-01T00:00:00Z\")" + } +} +``` + ## google\_project\_iam\_audit\_config ```hcl @@ -119,6 +179,9 @@ will not be inferred from the provider. * `audit_log_config` - (Required only by google\_project\_iam\_audit\_config) The configuration for logging of each type of permission. This can be specified multiple times. Structure is documented below. +* `condition` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) An [IAM Condition](https://cloud.google.com/iam/docs/conditions-overview) for a given binding. You must be whitelisted for the IAM Conditions private beta in order to use them in Terraform. + Structure is documented below. + --- The `audit_log_config` block supports: @@ -127,6 +190,18 @@ The `audit_log_config` block supports: * `exempted_members` - (Optional) Identities that do not cause logging for this type of permission. The format is the same as that for `members`. +The `condition` block supports: + +* `expression` - (Required) Textual representation of an expression in Common Expression Language syntax. + +* `title` - (Required) A title for the expression, i.e. a short string describing its purpose. + +* `description` - (Optional) An optional description of the expression. This is a longer text which describes the expression, e.g. when hovered over it in a UI. + +~> **Warning:** Terraform considers the `role` and condition contents (`title`+`description`+`expression`) as the + identifier for the binding. This means that if any part of the condition is changed out-of-band, Terraform will + consider it to be an entirely different resource and will treat it as such. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are diff --git a/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown b/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown index 1b2ff1a490fe..30e44b495918 100644 --- a/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown +++ b/third_party/terraform/website/docs/r/google_service_account_iam.html.markdown @@ -64,7 +64,7 @@ resource "google_service_account_iam_binding" "admin-account-iam" { } ``` -With IAM Conditions ([beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)): +With IAM Conditions ([beta](https://terraform.io/docs/providers/google/provider_versions.html), Whitelist-only): ```hcl resource "google_service_account" "sa" { @@ -112,7 +112,7 @@ resource "google_service_account_iam_member" "gce-default-account-iam" { } ``` -With IAM Conditions ([beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)): +With IAM Conditions ([beta](https://terraform.io/docs/providers/google/provider_versions.html), Whitelist-only): ```hcl resource "google_service_account" "sa" { @@ -155,7 +155,7 @@ The following arguments are supported: * `policy_data` - (Required only by `google_service_account_iam_policy`) The policy data generated by a `google_iam_policy` data source. -* `condition` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) An [IAM Condition](https://cloud.google.com/iam/docs/conditions-overview) for a given binding. +* `condition` - (Optional, [Beta](https://terraform.io/docs/providers/google/provider_versions.html)) An [IAM Condition](https://cloud.google.com/iam/docs/conditions-overview) for a given binding. You must be whitelisted for the IAM Conditions private beta in order to use them in Terraform. Structure is documented below. The `condition` block supports: