From 65801be826b047f87d9f5c8a6283dc91bdf1efd7 Mon Sep 17 00:00:00 2001 From: emily Date: Wed, 15 May 2019 19:47:45 +0000 Subject: [PATCH] Add dataproc iam resources Signed-off-by: Modular Magician --- google-beta/iam_dataproc_cluster.go | 116 +++++++++ google-beta/iam_dataproc_job.go | 134 +++++++++++ google-beta/provider.go | 6 + .../resource_dataproc_cluster_iam_test.go | 226 ++++++++++++++++++ google-beta/resource_dataproc_job_iam_test.go | 223 +++++++++++++++++ ...esource_google_service_account_iam_test.go | 20 +- .../docs/r/dataproc_cluster_iam.html.markdown | 112 +++++++++ website/docs/r/dataproc_job_iam.html.markdown | 112 +++++++++ website/google.erb | 30 ++- 9 files changed, 967 insertions(+), 12 deletions(-) create mode 100644 google-beta/iam_dataproc_cluster.go create mode 100644 google-beta/iam_dataproc_job.go create mode 100644 google-beta/resource_dataproc_cluster_iam_test.go create mode 100644 google-beta/resource_dataproc_job_iam_test.go create mode 100644 website/docs/r/dataproc_cluster_iam.html.markdown create mode 100644 website/docs/r/dataproc_job_iam.html.markdown diff --git a/google-beta/iam_dataproc_cluster.go b/google-beta/iam_dataproc_cluster.go new file mode 100644 index 0000000000..c226cc9ed5 --- /dev/null +++ b/google-beta/iam_dataproc_cluster.go @@ -0,0 +1,116 @@ +package google + +import ( + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/dataproc/v1" +) + +var IamDataprocClusterSchema = map[string]*schema.Schema{ + "cluster": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, +} + +type DataprocClusterIamUpdater struct { + project string + region string + cluster string + Config *Config +} + +func NewDataprocClusterUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { + project, err := getProject(d, config) + if err != nil { + return nil, err + } + + region, err := getRegion(d, config) + if err != nil { + return nil, err + } + + d.Set("project", project) + d.Set("region", region) + + return &DataprocClusterIamUpdater{ + project: project, + region: region, + cluster: d.Get("cluster").(string), + Config: config, + }, nil +} + +func DataprocClusterIdParseFunc(d *schema.ResourceData, config *Config) error { + fv, err := parseRegionalFieldValue("clusters", d.Id(), "project", "region", "zone", d, config, true) + if err != nil { + return err + } + + d.Set("project", fv.Project) + d.Set("region", fv.Region) + d.Set("cluster", fv.Name) + + // Explicitly set the id so imported resources have the same ID format as non-imported ones. + d.SetId(fv.RelativeLink()) + return nil +} + +func (u *DataprocClusterIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + req := &dataproc.GetIamPolicyRequest{} + p, err := u.Config.clientDataproc.Projects.Regions.Clusters.GetIamPolicy(u.GetResourceId(), req).Do() + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + cloudResourcePolicy, err := dataprocToResourceManagerPolicy(p) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return cloudResourcePolicy, nil +} + +func (u *DataprocClusterIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + dataprocPolicy, err := resourceManagerToDataprocPolicy(policy) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + req := &dataproc.SetIamPolicyRequest{Policy: dataprocPolicy} + _, err = u.Config.clientDataproc.Projects.Regions.Clusters.SetIamPolicy(u.GetResourceId(), req).Do() + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *DataprocClusterIamUpdater) GetResourceId() string { + return fmt.Sprintf("projects/%s/regions/%s/clusters/%s", u.project, u.region, u.cluster) +} + +func (u *DataprocClusterIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-dataproc-cluster-%s-%s-%s", u.project, u.region, u.cluster) +} + +func (u *DataprocClusterIamUpdater) DescribeResource() string { + return fmt.Sprintf("Dataproc Cluster %s/%s/%s", u.project, u.region, u.cluster) +} diff --git a/google-beta/iam_dataproc_job.go b/google-beta/iam_dataproc_job.go new file mode 100644 index 0000000000..153cb2cae4 --- /dev/null +++ b/google-beta/iam_dataproc_job.go @@ -0,0 +1,134 @@ +package google + +import ( + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/dataproc/v1" +) + +var IamDataprocJobSchema = map[string]*schema.Schema{ + "job_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, +} + +type DataprocJobIamUpdater struct { + project string + region string + jobId string + Config *Config +} + +func NewDataprocJobUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { + project, err := getProject(d, config) + if err != nil { + return nil, err + } + + region, err := getRegion(d, config) + if err != nil { + return nil, err + } + + d.Set("project", project) + d.Set("region", region) + + return &DataprocJobIamUpdater{ + project: project, + region: region, + jobId: d.Get("job_id").(string), + Config: config, + }, nil +} + +func DataprocJobIdParseFunc(d *schema.ResourceData, config *Config) error { + fv, err := parseRegionalFieldValue("jobs", d.Id(), "project", "region", "zone", d, config, true) + if err != nil { + return err + } + + d.Set("job_id", fv.Name) + d.Set("project", fv.Project) + d.Set("region", fv.Region) + + // Explicitly set the id so imported resources have the same ID format as non-imported ones. + d.SetId(fv.RelativeLink()) + return nil +} + +func (u *DataprocJobIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + req := &dataproc.GetIamPolicyRequest{} + p, err := u.Config.clientDataproc.Projects.Regions.Jobs.GetIamPolicy(u.GetResourceId(), req).Do() + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + cloudResourcePolicy, err := dataprocToResourceManagerPolicy(p) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return cloudResourcePolicy, nil +} + +func (u *DataprocJobIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + dataprocPolicy, err := resourceManagerToDataprocPolicy(policy) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Invalid IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + req := &dataproc.SetIamPolicyRequest{Policy: dataprocPolicy} + _, err = u.Config.clientDataproc.Projects.Regions.Jobs.SetIamPolicy(u.GetResourceId(), req).Do() + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *DataprocJobIamUpdater) GetResourceId() string { + return fmt.Sprintf("projects/%s/regions/%s/jobs/%s", u.project, u.region, u.jobId) +} + +func (u *DataprocJobIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-dataproc-job-%s-%s-%s", u.project, u.region, u.jobId) +} + +func (u *DataprocJobIamUpdater) DescribeResource() string { + return fmt.Sprintf("Dataproc Job %s/%s/%s", u.project, u.region, u.jobId) +} + +func resourceManagerToDataprocPolicy(p *cloudresourcemanager.Policy) (*dataproc.Policy, error) { + out := &dataproc.Policy{} + err := Convert(p, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a dataproc policy to a cloudresourcemanager policy: {{err}}", err) + } + return out, nil +} + +func dataprocToResourceManagerPolicy(p *dataproc.Policy) (*cloudresourcemanager.Policy, error) { + out := &cloudresourcemanager.Policy{} + err := Convert(p, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a cloudresourcemanager policy to a dataproc policy: {{err}}", err) + } + return out, nil +} diff --git a/google-beta/provider.go b/google-beta/provider.go index ba753995a1..6fba795d9e 100644 --- a/google-beta/provider.go +++ b/google-beta/provider.go @@ -200,7 +200,13 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_container_node_pool": resourceContainerNodePool(), "google_dataflow_job": resourceDataflowJob(), "google_dataproc_cluster": resourceDataprocCluster(), + "google_dataproc_cluster_iam_binding": ResourceIamBindingWithImport(IamDataprocClusterSchema, NewDataprocClusterUpdater, DataprocClusterIdParseFunc), + "google_dataproc_cluster_iam_member": ResourceIamMemberWithImport(IamDataprocClusterSchema, NewDataprocClusterUpdater, DataprocClusterIdParseFunc), + "google_dataproc_cluster_iam_policy": ResourceIamPolicyWithImport(IamDataprocClusterSchema, NewDataprocClusterUpdater, DataprocClusterIdParseFunc), "google_dataproc_job": resourceDataprocJob(), + "google_dataproc_job_iam_binding": ResourceIamBindingWithImport(IamDataprocJobSchema, NewDataprocJobUpdater, DataprocJobIdParseFunc), + "google_dataproc_job_iam_member": ResourceIamMemberWithImport(IamDataprocJobSchema, NewDataprocJobUpdater, DataprocJobIdParseFunc), + "google_dataproc_job_iam_policy": ResourceIamPolicyWithImport(IamDataprocJobSchema, NewDataprocJobUpdater, DataprocJobIdParseFunc), "google_dns_record_set": resourceDnsRecordSet(), "google_endpoints_service": resourceEndpointsService(), "google_folder": resourceGoogleFolder(), diff --git a/google-beta/resource_dataproc_cluster_iam_test.go b/google-beta/resource_dataproc_cluster_iam_test.go new file mode 100644 index 0000000000..3594976ad5 --- /dev/null +++ b/google-beta/resource_dataproc_cluster_iam_test.go @@ -0,0 +1,226 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataprocClusterIamBinding(t *testing.T) { + t.Parallel() + + cluster := "tf-dataproc-iam-" + acctest.RandString(10) + account := "tf-dataproc-iam-" + acctest.RandString(10) + role := "roles/editor" + + importId := fmt.Sprintf("projects/%s/regions/%s/clusters/%s %s", + getTestProjectFromEnv(), "us-central1", cluster, role) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Binding creation + Config: testAccDataprocClusterIamBinding_basic(cluster, account, role), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "google_dataproc_cluster_iam_binding.binding", "role", role), + ), + }, + { + ResourceName: "google_dataproc_cluster_iam_binding.binding", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test IAM Binding update + Config: testAccDataprocClusterIamBinding_update(cluster, account, role), + }, + { + ResourceName: "google_dataproc_cluster_iam_binding.binding", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccDataprocClusterIamMember(t *testing.T) { + t.Parallel() + + cluster := "tf-dataproc-iam-" + acctest.RandString(10) + account := "tf-dataproc-iam-" + acctest.RandString(10) + role := "roles/editor" + + importId := fmt.Sprintf("projects/%s/regions/%s/clusters/%s %s serviceAccount:%s", + getTestProjectFromEnv(), + "us-central1", + cluster, + role, + serviceAccountCanonicalEmail(account)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Binding creation + Config: testAccDataprocClusterIamMember(cluster, account, role), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "google_dataproc_cluster_iam_member.member", "role", role), + resource.TestCheckResourceAttr( + "google_dataproc_cluster_iam_member.member", "member", "serviceAccount:"+serviceAccountCanonicalEmail(account)), + ), + }, + { + ResourceName: "google_dataproc_cluster_iam_member.member", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccDataprocClusterIamPolicy(t *testing.T) { + t.Parallel() + + cluster := "tf-dataproc-iam-" + acctest.RandString(10) + account := "tf-dataproc-iam-" + acctest.RandString(10) + role := "roles/editor" + + importId := fmt.Sprintf("projects/%s/regions/%s/clusters/%s", + getTestProjectFromEnv(), "us-central1", cluster) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Binding creation + Config: testAccDataprocClusterIamPolicy(cluster, account, role), + }, + { + ResourceName: "google_dataproc_cluster_iam_policy.policy", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccDataprocClusterIamBinding_basic(cluster, account, role string) string { + return fmt.Sprintf(testDataprocIamSingleNodeCluster+` + +resource "google_service_account" "test-account1" { + account_id = "%s-1" + display_name = "Dataproc IAM Testing Account" +} + +resource "google_service_account" "test-account2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + +resource "google_dataproc_cluster_iam_binding" "binding" { + cluster = "${google_dataproc_cluster.cluster.name}" + region = "us-central1" + role = "%s" + members = [ + "serviceAccount:${google_service_account.test-account1.email}", + ] +} +`, cluster, account, account, role) +} + +func testAccDataprocClusterIamBinding_update(cluster, account, role string) string { + return fmt.Sprintf(testDataprocIamSingleNodeCluster+` +resource "google_service_account" "test-account1" { + account_id = "%s-1" + display_name = "Dataproc IAM Testing Account" +} + +resource "google_service_account" "test-account2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + +resource "google_dataproc_cluster_iam_binding" "binding" { + cluster = "${google_dataproc_cluster.cluster.name}" + region = "us-central1" + role = "%s" + members = [ + "serviceAccount:${google_service_account.test-account1.email}", + "serviceAccount:${google_service_account.test-account2.email}", + ] +} +`, cluster, account, account, role) +} + +func testAccDataprocClusterIamMember(cluster, account, role string) string { + return fmt.Sprintf(testDataprocIamSingleNodeCluster+` +resource "google_service_account" "test-account" { + account_id = "%s" + display_name = "Dataproc IAM Testing Account" +} + +resource "google_dataproc_cluster_iam_member" "member" { + cluster = "${google_dataproc_cluster.cluster.name}" + role = "%s" + member = "serviceAccount:${google_service_account.test-account.email}" +} +`, cluster, account, role) +} + +func testAccDataprocClusterIamPolicy(cluster, account, role string) string { + return fmt.Sprintf(testDataprocIamSingleNodeCluster+` +resource "google_service_account" "test-account" { + account_id = "%s" + display_name = "Dataproc IAM Testing Account" +} + +data "google_iam_policy" "policy" { + binding { + role = "%s" + members = ["serviceAccount:${google_service_account.test-account.email}"] + } +} + +resource "google_dataproc_cluster_iam_policy" "policy" { + cluster = "${google_dataproc_cluster.cluster.name}" + region = "us-central1" + policy_data = "${data.google_iam_policy.policy.policy_data}" +} +`, cluster, account, role) +} + +// Smallest cluster possible for testing +var testDataprocIamSingleNodeCluster = ` +resource "google_dataproc_cluster" "cluster" { + name = "%s" + region = "us-central1" + + cluster_config { + # Keep the costs down with smallest config we can get away with + software_config { + override_properties = { + "dataproc:dataproc.allow.zero.workers" = "true" + } + } + + master_config { + num_instances = 1 + machine_type = "n1-standard-1" + disk_config { + boot_disk_size_gb = 15 + } + } + } +}` diff --git a/google-beta/resource_dataproc_job_iam_test.go b/google-beta/resource_dataproc_job_iam_test.go new file mode 100644 index 0000000000..c34f0640a3 --- /dev/null +++ b/google-beta/resource_dataproc_job_iam_test.go @@ -0,0 +1,223 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataprocJobIamBinding(t *testing.T) { + t.Parallel() + + cluster := "tf-dataproc-iam-cluster" + acctest.RandString(10) + job := "tf-dataproc-iam-job-" + acctest.RandString(10) + account := "tf-dataproc-iam-" + acctest.RandString(10) + role := "roles/editor" + + importId := fmt.Sprintf("projects/%s/regions/%s/jobs/%s %s", + getTestProjectFromEnv(), "us-central1", job, role) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Binding creation + Config: testAccDataprocJobIamBinding_basic(cluster, job, account, role), + }, + { + ResourceName: "google_dataproc_job_iam_binding.binding", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + { + // Test IAM Binding update + Config: testAccDataprocJobIamBinding_update(cluster, job, account, role), + }, + { + ResourceName: "google_dataproc_job_iam_binding.binding", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccDataprocJobIamMember(t *testing.T) { + t.Parallel() + + cluster := "tf-dataproc-iam-cluster" + acctest.RandString(10) + job := "tf-dataproc-iam-jobid-" + acctest.RandString(10) + account := "tf-dataproc-iam-" + acctest.RandString(10) + role := "roles/editor" + + importId := fmt.Sprintf("projects/%s/regions/%s/jobs/%s %s serviceAccount:%s", + getTestProjectFromEnv(), + "us-central1", + job, + role, + serviceAccountCanonicalEmail(account)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Binding creation + Config: testAccDataprocJobIamMember(cluster, job, account, role), + }, + { + ResourceName: "google_dataproc_job_iam_member.member", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccDataprocJobIamPolicy(t *testing.T) { + t.Parallel() + + cluster := "tf-dataproc-iam-cluster" + acctest.RandString(10) + job := "tf-dataproc-iam-jobid-" + acctest.RandString(10) + account := "tf-dataproc-iam-" + acctest.RandString(10) + role := "roles/editor" + + importId := fmt.Sprintf("projects/%s/regions/%s/jobs/%s", + getTestProjectFromEnv(), "us-central1", job) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test IAM Binding creation + Config: testAccDataprocJobIamPolicy(cluster, job, account, role), + }, + { + ResourceName: "google_dataproc_job_iam_policy.policy", + ImportStateId: importId, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +var testDataprocIamJobConfig = testDataprocIamSingleNodeCluster + ` +resource "google_dataproc_job" "pyspark" { + region = "${google_dataproc_cluster.cluster.region}" + + placement { + cluster_name = "${google_dataproc_cluster.cluster.name}" + } + + reference { + job_id = "%s" + } + + force_delete = true + + pyspark_config { + main_python_file_uri = "gs://dataproc-examples-2f10d78d114f6aaec76462e3c310f31f/src/pyspark/hello-world/hello-world.py" + properties = { + "spark.logConf" = "true" + } + logging_config { + driver_log_levels = { + "root" = "INFO" + } + } + } +} +` + +func testAccDataprocJobIamBinding_basic(cluster, job, account, role string) string { + return fmt.Sprintf(testDataprocIamJobConfig+` +resource "google_service_account" "test-account1" { + account_id = "%s-1" + display_name = "Dataproc IAM Testing Account" +} + +resource "google_service_account" "test-account2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + +resource "google_dataproc_job_iam_binding" "binding" { + job_id = "${google_dataproc_job.pyspark.reference.0.job_id}" + region = "us-central1" + role = "%s" + members = [ + "serviceAccount:${google_service_account.test-account1.email}", + ] +} + +`, cluster, job, account, account, role) +} + +func testAccDataprocJobIamBinding_update(cluster, job, account, role string) string { + return fmt.Sprintf(testDataprocIamJobConfig+` +resource "google_service_account" "test-account1" { + account_id = "%s-1" + display_name = "Dataproc IAM Testing Account" +} + +resource "google_service_account" "test-account2" { + account_id = "%s-2" + display_name = "Iam Testing Account" +} + +resource "google_dataproc_job_iam_binding" "binding" { + job_id = "${google_dataproc_job.pyspark.reference.0.job_id}" + region = "us-central1" + role = "%s" + members = [ + "serviceAccount:${google_service_account.test-account1.email}", + "serviceAccount:${google_service_account.test-account2.email}", + ] +} +`, cluster, job, account, account, role) +} + +func testAccDataprocJobIamMember(cluster, job, account, role string) string { + return fmt.Sprintf(testDataprocIamJobConfig+` +resource "google_service_account" "test-account" { + account_id = "%s" + display_name = "Dataproc IAM Testing Account" +} + +resource "google_dataproc_job_iam_member" "member" { + job_id = "${google_dataproc_job.pyspark.reference.0.job_id}" + role = "%s" + member = "serviceAccount:${google_service_account.test-account.email}" +} +`, cluster, job, account, role) +} + +func testAccDataprocJobIamPolicy(cluster, job, account, role string) string { + return fmt.Sprintf(testDataprocIamJobConfig+` +resource "google_service_account" "test-account" { + account_id = "%s" + display_name = "Dataproc IAM Testing Account" +} + +data "google_iam_policy" "policy" { + binding { + role = "%s" + members = ["serviceAccount:${google_service_account.test-account.email}"] + } +} + +resource "google_dataproc_job_iam_policy" "policy" { + job_id = "${google_dataproc_job.pyspark.reference.0.job_id}" + region = "us-central1" + policy_data = "${data.google_iam_policy.policy.policy_data}" +} +`, cluster, job, account, role) +} diff --git a/google-beta/resource_google_service_account_iam_test.go b/google-beta/resource_google_service_account_iam_test.go index 9164c34ce8..d57ae4582c 100644 --- a/google-beta/resource_google_service_account_iam_test.go +++ b/google-beta/resource_google_service_account_iam_test.go @@ -23,12 +23,12 @@ func TestAccServiceAccountIamBinding(t *testing.T) { { Config: testAccServiceAccountIamBinding_basic(account), Check: testAccCheckGoogleServiceAccountIam(account, "roles/viewer", []string{ - fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + fmt.Sprintf("serviceAccount:%s", serviceAccountCanonicalEmail(account)), }), }, { ResourceName: "google_service_account_iam_binding.foo", - ImportStateId: fmt.Sprintf("%s %s", getServiceAccountCanonicalId(account), "roles/viewer"), + ImportStateId: fmt.Sprintf("%s %s", serviceAccountCanonicalId(account), "roles/viewer"), ImportState: true, ImportStateVerify: true, }, @@ -40,7 +40,7 @@ func TestAccServiceAccountIamMember(t *testing.T) { t.Parallel() account := acctest.RandomWithPrefix("tf-test") - identity := fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()) + identity := fmt.Sprintf("serviceAccount:%s", serviceAccountCanonicalEmail(account)) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -52,7 +52,7 @@ func TestAccServiceAccountIamMember(t *testing.T) { }, { ResourceName: "google_service_account_iam_member.foo", - ImportStateId: fmt.Sprintf("%s %s %s", getServiceAccountCanonicalId(account), "roles/editor", identity), + ImportStateId: fmt.Sprintf("%s %s %s", serviceAccountCanonicalId(account), "roles/editor", identity), ImportState: true, ImportStateVerify: true, }, @@ -72,12 +72,12 @@ func TestAccServiceAccountIamPolicy(t *testing.T) { { Config: testAccServiceAccountIamPolicy_basic(account), Check: testAccCheckGoogleServiceAccountIam(account, "roles/owner", []string{ - fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), + fmt.Sprintf("serviceAccount:%s", serviceAccountCanonicalEmail(account)), }), }, { ResourceName: "google_service_account_iam_policy.foo", - ImportStateId: getServiceAccountCanonicalId(account), + ImportStateId: serviceAccountCanonicalId(account), ImportState: true, ImportStateVerify: true, }, @@ -88,7 +88,7 @@ func TestAccServiceAccountIamPolicy(t *testing.T) { func testAccCheckGoogleServiceAccountIam(account, role string, members []string) resource.TestCheckFunc { return func(s *terraform.State) error { config := testAccProvider.Meta().(*Config) - p, err := config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(getServiceAccountCanonicalId(account)).Do() + p, err := config.clientIAM.Projects.ServiceAccounts.GetIamPolicy(serviceAccountCanonicalId(account)).Do() if err != nil { return err } @@ -110,10 +110,14 @@ func testAccCheckGoogleServiceAccountIam(account, role string, members []string) } } -func getServiceAccountCanonicalId(account string) string { +func serviceAccountCanonicalId(account string) string { return fmt.Sprintf("projects/%s/serviceAccounts/%s@%s.iam.gserviceaccount.com", getTestProjectFromEnv(), account, getTestProjectFromEnv()) } +func serviceAccountCanonicalEmail(account string) string { + return fmt.Sprintf("%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()) +} + func testAccServiceAccountIamBinding_basic(account string) string { return fmt.Sprintf(` resource "google_service_account" "test_account" { diff --git a/website/docs/r/dataproc_cluster_iam.html.markdown b/website/docs/r/dataproc_cluster_iam.html.markdown new file mode 100644 index 0000000000..afe72c5697 --- /dev/null +++ b/website/docs/r/dataproc_cluster_iam.html.markdown @@ -0,0 +1,112 @@ +--- +layout: "google" +page_title: "Google: google_dataproc_cluster_iam" +sidebar_current: "docs-google-dataproc-cluster-iam" +description: |- + Collection of resources to manage IAM policy for a Dataproc cluster. +--- + +# IAM policy for Dataproc cluster + +Three different resources help you manage IAM policies on dataproc clusters. Each of these resources serves a different use case: + +* `google_dataproc_cluster_iam_policy`: Authoritative. Sets the IAM policy for the cluster and replaces any existing policy already attached. +* `google_dataproc_cluster_iam_binding`: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the cluster are preserved. +* `google_dataproc_cluster_iam_member`: Non-authoritative. Updates the IAM policy to grant a role to a new member. Other members for the role for the cluster are preserved. + +~> **Note:** `google_dataproc_cluster_iam_policy` **cannot** be used in conjunction with `google_dataproc_cluster_iam_binding` and `google_dataproc_cluster_iam_member` or they will fight over what your policy should be. In addition, be careful not to accidentaly unset ownership of the cluster as `google_dataproc_cluster_iam_policy` replaces the entire policy. + +~> **Note:** `google_dataproc_cluster_iam_binding` resources **can be** used in conjunction with `google_dataproc_cluster_iam_member` resources **only if** they do not grant privilege to the same role. + +## google\_pubsub\_subscription\_iam\_policy + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/editor" + members = [ + "user:jane@example.com", + ] + } +} + +resource "google_dataproc_cluster_iam_policy" "editor" { + project = "your-project" + region = "your-region" + cluster = "your-dataproc-cluster" + policy_data = "${data.google_iam_policy.admin.policy_data}" +} +``` + +## google\_pubsub\_subscription\_iam\_binding + +```hcl +resource "google_dataproc_cluster_iam_binding" "editor" { + cluster = "your-dataproc-cluster" + role = "roles/editor" + members = [ + "user:jane@example.com", + ] +} +``` + +## google\_pubsub\_subscription\_iam\_member + +```hcl +resource "google_dataproc_cluster_iam_member" "editor" { + cluster = "your-dataproc-cluster" + role = "roles/editor" + member = "user:jane@example.com" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `cluster` - (Required) The name or relative resource id of the cluster to manage IAM policies for. + +For `google_dataproc_cluster_iam_member` or `google_dataproc_cluster_iam_binding`: + +* `member/members` - (Required) Identities that will be granted the privilege in `role`. + Each entry can have one of the following values: + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. + * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + +* `role` - (Required) The role that should be applied. Only one + `google_dataproc_cluster_iam_binding` can be used per role. Note that custom roles must be of the format + `[projects|organizations]/{parent-name}/roles/{role-name}`. + +`google_dataproc_cluster_iam_policy` only: +* `policy_data` - (Required) The policy data generated by a `google_iam_policy` data source. + +- - - + +* `project` - (Optional) The project in which the cluster belongs. If it + is not provided, Terraform will use the provider default. + +* `region` - (Optional) The region in which the cluster belongs. If it + is not provided, Terraform will use the provider default. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `etag` - (Computed) The etag of the clusters's IAM policy. + +## Import + +Cluster IAM resources can be imported using the project, region, cluster name, role and/or member. + +``` +$ terraform import google_dataproc_cluster_iam_policy.editor "projects/{project}/regions/{region}/clusters/{cluster}" + +$ terraform import google_dataproc_cluster_iam_binding.editor "projects/{project}/regions/{region}/clusters/{cluster} roles/editor" + +$ terraform import google_dataproc_cluster_iam_member.editor "projects/{project}/regions/{region}/clusters/{cluster} roles/editor user:jane@example.com" +``` \ No newline at end of file diff --git a/website/docs/r/dataproc_job_iam.html.markdown b/website/docs/r/dataproc_job_iam.html.markdown new file mode 100644 index 0000000000..69cc13a021 --- /dev/null +++ b/website/docs/r/dataproc_job_iam.html.markdown @@ -0,0 +1,112 @@ +--- +layout: "google" +page_title: "Google: google_dataproc_job_iam" +sidebar_current: "docs-google-dataproc-job-iam" +description: |- + Collection of resources to manage IAM policy for a Dataproc job. +--- + +# IAM policy for Dataproc job + +Three different resources help you manage IAM policies on dataproc jobs. Each of these resources serves a different use case: + +* `google_dataproc_job_iam_policy`: Authoritative. Sets the IAM policy for the job and replaces any existing policy already attached. +* `google_dataproc_job_iam_binding`: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the job are preserved. +* `google_dataproc_job_iam_member`: Non-authoritative. Updates the IAM policy to grant a role to a new member. Other members for the role for the job are preserved. + +~> **Note:** `google_dataproc_job_iam_policy` **cannot** be used in conjunction with `google_dataproc_job_iam_binding` and `google_dataproc_job_iam_member` or they will fight over what your policy should be. In addition, be careful not to accidentaly unset ownership of the job as `google_dataproc_job_iam_policy` replaces the entire policy. + +~> **Note:** `google_dataproc_job_iam_binding` resources **can be** used in conjunction with `google_dataproc_job_iam_member` resources **only if** they do not grant privilege to the same role. + +## google\_pubsub\_subscription\_iam\_policy + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/editor" + members = [ + "user:jane@example.com", + ] + } +} + +resource "google_dataproc_job_iam_policy" "editor" { + project = "your-project" + region = "your-region" + job_id = "your-dataproc-job" + policy_data = "${data.google_iam_policy.admin.policy_data}" +} +``` + +## google\_pubsub\_subscription\_iam\_binding + +```hcl +resource "google_dataproc_job_iam_binding" "editor" { + job_id = "your-dataproc-job" + role = "roles/editor" + members = [ + "user:jane@example.com", + ] +} +``` + +## google\_pubsub\_subscription\_iam\_member + +```hcl +resource "google_dataproc_job_iam_member" "editor" { + job_id = "your-dataproc-job" + role = "roles/editor" + member = "user:jane@example.com" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `job` - (Required) The name or relative resource id of the job to manage IAM policies for. + +For `google_dataproc_job_iam_member` or `google_dataproc_job_iam_binding`: + +* `member/members` - (Required) Identities that will be granted the privilege in `role`. + Each entry can have one of the following values: + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. + * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + +* `role` - (Required) The role that should be applied. Only one + `google_dataproc_job_iam_binding` can be used per role. Note that custom roles must be of the format + `[projects|organizations]/{parent-name}/roles/{role-name}`. + +`google_dataproc_job_iam_policy` only: +* `policy_data` - (Required) The policy data generated by a `google_iam_policy` data source. + +- - - + +* `project` - (Optional) The project in which the job belongs. If it + is not provided, Terraform will use the provider default. + +* `region` - (Optional) The region in which the job belongs. If it + is not provided, Terraform will use the provider default. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `etag` - (Computed) The etag of the jobs's IAM policy. + +## Import + +Job IAM resources can be imported using the project, region, job id, role and/or member. + +``` +$ terraform import google_dataproc_job_iam_policy.editor "projects/{project}/regions/{region}/jobs/{job_id}" + +$ terraform import google_dataproc_job_iam_binding.editor "projects/{project}/regions/{region}/jobs/{job_id} roles/editor" + +$ terraform import google_dataproc_job_iam_member.editor "projects/{project}/regions/{region}/jobs/{job_id} roles/editor user:jane@example.com" +``` \ No newline at end of file diff --git a/website/google.erb b/website/google.erb index 1590a33bee..2d5735121d 100644 --- a/website/google.erb +++ b/website/google.erb @@ -647,12 +647,34 @@ > google_dataproc_cluster - - >