diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000000..518e76a2af --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,4 @@ +MD013: false +MD024: + siblings_only: true +MD028: false diff --git a/github/data_source_github_organization_custom_role.go b/github/data_source_github_organization_custom_role.go index a2ae97ffeb..24472a8c17 100644 --- a/github/data_source_github_organization_custom_role.go +++ b/github/data_source_github_organization_custom_role.go @@ -12,8 +12,9 @@ import ( func dataSourceGithubOrganizationCustomRole() *schema.Resource { return &schema.Resource{ - Read: dataSourceGithubOrganizationCustomRoleRead, + DeprecationMessage: "This data source is deprecated and will be removed in a future release. Use the github_organization_repository_role data source instead.", + Read: dataSourceGithubOrganizationCustomRoleRead, Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, diff --git a/github/data_source_github_organization_repository_role.go b/github/data_source_github_organization_repository_role.go new file mode 100644 index 0000000000..5fdb6218f7 --- /dev/null +++ b/github/data_source_github_organization_repository_role.go @@ -0,0 +1,95 @@ +package github + +import ( + "context" + "fmt" + "strconv" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubOrganizationRepositoryRole() *schema.Resource { + return &schema.Resource{ + Description: "Lookup a custom organization repository role.", + + Read: dataSourceGithubOrganizationRepositoryRoleRead, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization repository role.", + Type: schema.TypeInt, + Required: true, + }, + "name": { + Description: "The name of the organization repository role.", + Type: schema.TypeString, + Computed: true, + }, + "description": { + Description: "The description of the organization repository role.", + Type: schema.TypeString, + Computed: true, + }, + "base_role": { + Description: "The system role from which this role inherits permissions.", + Type: schema.TypeString, + Computed: true, + }, + "permissions": { + Description: "The permissions included in this role.", + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + }, + } +} + +func dataSourceGithubOrganizationRepositoryRoleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + + // TODO: Use this code when go-github adds the functionality to get a custom repo role + // role, _, err := client.Organizations.GetCustomRepoRole(ctx, orgName, roleId) + // if err != nil { + // return err + // } + + roles, _, err := client.Organizations.ListCustomRepoRoles(ctx, orgName) + if err != nil { + return err + } + + var role *github.CustomRepoRoles + for _, r := range roles.CustomRepoRoles { + if r.GetID() == roleId { + role = r + break + } + } + if role == nil { + return fmt.Errorf("custom organization repo role with ID %d not found", roleId) + } + + r := map[string]any{ + "role_id": role.GetID(), + "name": role.GetName(), + "description": role.GetDescription(), + "base_role": role.GetBaseRole(), + "permissions": role.Permissions, + } + + d.SetId(strconv.FormatInt(role.GetID(), 10)) + + for k, v := range r { + if err := d.Set(k, v); err != nil { + return err + } + } + + return nil +} diff --git a/github/data_source_github_organization_repository_role_test.go b/github/data_source_github_organization_repository_role_test.go new file mode 100644 index 0000000000..377554d9d3 --- /dev/null +++ b/github/data_source_github_organization_repository_role_test.go @@ -0,0 +1,50 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubOrganizationRepositoryRoleDataSource(t *testing.T) { + t.Run("queries an organization repository role", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + roleName := fmt.Sprintf(`tf-acc-test-%s`, randomID) + + config := fmt.Sprintf(` + resource "github_organization_repository_role" "test" { + name = "%s" + description = "Test role description" + base_role = "read" + permissions = [ + "reopen_issue", + "reopen_pull_request", + ] + } + + data "github_organization_repository_role" "test" { + role_id = github_organization_repository_role.test.role_id + + depends_on = [ github_organization_repository_role.test ] + } + `, roleName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, enterprise) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair("data.github_organization_repository_role.test", "name", "github_organization_repository_role.test", "name"), + resource.TestCheckResourceAttrPair("data.github_organization_repository_role.test", "description", "github_organization_repository_role.test", "description"), + resource.TestCheckResourceAttrPair("data.github_organization_repository_role.test", "base_role", "github_organization_repository_role.test", "base_role"), + resource.TestCheckResourceAttrPair("data.github_organization_repository_role.test", "permissions.#", "github_organization_repository_role.test", "permissions.#"), + ), + }, + }, + }) + }) +} diff --git a/github/data_source_github_organization_repository_roles.go b/github/data_source_github_organization_repository_roles.go new file mode 100644 index 0000000000..11a8e7b687 --- /dev/null +++ b/github/data_source_github_organization_repository_roles.go @@ -0,0 +1,84 @@ +package github + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubOrganizationRepositoryRoles() *schema.Resource { + return &schema.Resource{ + Description: "Lookup all custom repository roles in an organization.", + + Read: dataSourceGithubOrganizationRepositoryRolesRead, + + Schema: map[string]*schema.Schema{ + "roles": { + Description: "Available organization repository roles.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization repository role.", + Type: schema.TypeInt, + Computed: true, + }, + "name": { + Description: "The name of the organization repository role.", + Type: schema.TypeString, + Computed: true, + }, + "description": { + Description: "The description of the organization repository role.", + Type: schema.TypeString, + Computed: true, + }, + "base_role": { + Description: "The system role from which this role inherits permissions.", + Type: schema.TypeString, + Computed: true, + }, + "permissions": { + Description: "The permissions included in this role.", + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceGithubOrganizationRepositoryRolesRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + ret, _, err := client.Organizations.ListCustomRepoRoles(ctx, orgName) + if err != nil { + return err + } + + allRoles := make([]any, ret.GetTotalCount()) + for i, role := range ret.CustomRepoRoles { + r := map[string]any{ + "role_id": role.GetID(), + "name": role.GetName(), + "description": role.GetDescription(), + "base_role": role.GetBaseRole(), + "permissions": role.Permissions, + } + allRoles[i] = r + } + + d.SetId(fmt.Sprintf("%s/github-org-repo-roles", orgName)) + if err := d.Set("roles", allRoles); err != nil { + return fmt.Errorf("error setting roles: %s", err) + } + + return nil +} diff --git a/github/data_source_github_organization_repository_roles_test.go b/github/data_source_github_organization_repository_roles_test.go new file mode 100644 index 0000000000..d205c79176 --- /dev/null +++ b/github/data_source_github_organization_repository_roles_test.go @@ -0,0 +1,65 @@ +package github + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceGithubOrganizationRepositoryRoles(t *testing.T) { + t.Run("get empty organization roles without error", func(t *testing.T) { + config := ` + data "github_organization_repository_roles" "test" {} + ` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_repository_roles.test", "roles.#"), + resource.TestCheckResourceAttr("data.github_organization_repository_roles.test", "roles.#", "0"), + ), + }, + }, + }) + }) + + t.Run("get organization roles without error", func(t *testing.T) { + config := ` + resource "github_organization_repository_role" "test" { + name = "%s" + description = "Test role description" + base_role = "read" + permissions = [ + "reopen_issue", + "reopen_pull_request", + ] + } + + data "github_organization_repository_roles" "test" { + depends_on = [ github_organization_repository_role.test ] + } + ` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, enterprise) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_repository_roles.test", "roles.#"), + resource.TestCheckResourceAttr("data.github_organization_repository_roles.test", "roles.#", "1"), + resource.TestCheckResourceAttrPair("data.github_organization_repository_roles.test", "roles.0.name", "github_organization_repository_role.test", "name"), + resource.TestCheckResourceAttrPair("data.github_organization_repository_roles.test", "roles.0.description", "github_organization_repository_role.test", "description"), + resource.TestCheckResourceAttrPair("data.github_organization_repository_roles.test", "roles.0.base_role", "github_organization_repository_role.test", "base_role"), + resource.TestCheckResourceAttrPair("data.github_organization_repository_roles.test", "roles.0.permissions.#", "github_organization_repository_role.test", "permissions.#"), + ), + }, + }, + }) + }) +} diff --git a/github/data_source_github_organization_role.go b/github/data_source_github_organization_role.go new file mode 100644 index 0000000000..58dc86daaa --- /dev/null +++ b/github/data_source_github_organization_role.go @@ -0,0 +1,82 @@ +package github + +import ( + "context" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubOrganizationRole() *schema.Resource { + return &schema.Resource{ + Description: "Lookup a custom organization role.", + + Read: dataSourceGithubOrganizationRoleRead, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization role.", + Type: schema.TypeInt, + Required: true, + }, + "name": { + Description: "The name of the organization role.", + Type: schema.TypeString, + Computed: true, + }, + "description": { + Description: "The description of the organization role.", + Type: schema.TypeString, + Computed: true, + }, + "source": { + Description: "The source of this role; one of `Predefined`, `Organization`, or `Enterprise`.", + Type: schema.TypeString, + Computed: true, + }, + "base_role": { + Description: "The system role from which this role inherits permissions.", + Type: schema.TypeString, + Computed: true, + }, + "permissions": { + Description: "A list of permissions included in this role.", + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + }, + } +} + +func dataSourceGithubOrganizationRoleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + + role, _, err := client.Organizations.GetOrgRole(ctx, orgName, roleId) + if err != nil { + return err + } + + r := map[string]any{ + "role_id": role.GetID(), + "name": role.GetName(), + "description": role.GetDescription(), + "source": role.GetSource(), + "base_role": role.GetBaseRole(), + "permissions": role.Permissions, + } + + d.SetId(strconv.FormatInt(role.GetID(), 10)) + + for k, v := range r { + if err := d.Set(k, v); err != nil { + return err + } + } + + return nil +} diff --git a/github/data_source_github_organization_role_teams.go b/github/data_source_github_organization_role_teams.go new file mode 100644 index 0000000000..d19404523f --- /dev/null +++ b/github/data_source_github_organization_role_teams.go @@ -0,0 +1,115 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubOrganizationRoleTeams() *schema.Resource { + return &schema.Resource{ + Description: "Lookup all teams assigned to a custom organization role.", + + Read: dataSourceGithubOrganizationRoleTeamsRead, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The unique identifier of the organization role.", + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "teams": { + Description: "Teams assigned to the organization role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "team_id": { + Description: "The ID of the team.", + Type: schema.TypeInt, + Computed: true, + }, + "slug": { + Description: "The Slug of the team name.", + Type: schema.TypeString, + Computed: true, + }, + "name": { + Description: "The name of the team.", + Type: schema.TypeString, + Computed: true, + }, + "permission": { + Description: "The permission that the team will have for its repositories.", + Type: schema.TypeString, + Computed: true, + }, + // TODO: Add these fields when go-github adds the functionality to get a custom org + // See https://github.com/google/go-github/issues/3364 + // "assignment": { + // Description: "Determines if the team has a direct, indirect, or mixed relationship to a role.", + // Type: schema.TypeString, + // Computed: true, + // }, + // "parent_team_id": { + // Description: "The ID of the parent team if this is an indirect assignment.", + // Type: schema.TypeString, + // Computed: true, + // }, + // "parent_team_slug": { + // Description: "The slug of the parent team if this is an indirect assignment.", + // Type: schema.TypeString, + // Computed: true, + // }, + }, + }, + }, + }, + } +} + +func dataSourceGithubOrganizationRoleTeamsRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + + allTeams := make([]any, 0) + + opts := &github.ListOptions{ + PerPage: maxPerPage, + } + + for { + teams, resp, err := client.Organizations.ListTeamsAssignedToOrgRole(ctx, orgName, roleId, opts) + if err != nil { + return err + } + + for _, team := range teams { + t := map[string]any{ + "id": team.GetID(), + "slug": team.GetSlug(), + "name": team.GetName(), + "permission": team.GetPermission(), + } + allTeams = append(allTeams, t) + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + + d.SetId(fmt.Sprintf("%d", roleId)) + if err := d.Set("teams", allTeams); err != nil { + return fmt.Errorf("error setting teams: %s", err) + } + + return nil +} diff --git a/github/data_source_github_organization_role_teams_test.go b/github/data_source_github_organization_role_teams_test.go new file mode 100644 index 0000000000..00f3f0e55b --- /dev/null +++ b/github/data_source_github_organization_role_teams_test.go @@ -0,0 +1,104 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceGithubOrganizationRoleTeams(t *testing.T) { + t.Run("get the organization role teams without error", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + teamName := fmt.Sprintf("tf-acc-team-%s", randomID) + roleId := 8134 + config := fmt.Sprintf(` + resource "github_team" "test" { + name = "%s" + } + + resource "github_organization_role_team" "test" { + role_id = %d + team_slug = github_team.test.slug + } + + data "github_organization_role_teams" "test" { + role_id = %[2]d + + depends_on = [ + github_organization_role_team.test + ] + } + `, teamName, roleId) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_role_teams.test", "teams.#"), + resource.TestCheckResourceAttr("data.github_organization_role_teams.test", "teams.#", "1"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.0.team_id", "github_team.test", "id"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.0.slug", "github_team.test", "slug"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.0.name", "github_team.test", "name"), + ), + }, + }, + }) + }) + + t.Run("get indirect organization role teams without error", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + teamName1 := fmt.Sprintf("tf-acc-team-1-%s", randomID) + teamName2 := fmt.Sprintf("tf-acc-team-2-%s", randomID) + roleId := 8134 + config := fmt.Sprintf(` + resource "github_team" "test_1" { + name = "%s" + privacy = "closed" + } + + resource "github_team" "test_2" { + name = "%s" + privacy = "closed" + parent_team_id = github_team.test_1.id + } + + resource "github_organization_role_team" "test" { + role_id = %d + team_slug = github_team.test_1.slug + } + + data "github_organization_role_teams" "test" { + role_id = %[3]d + + depends_on = [ + github_organization_role_team.test + ] + } + `, teamName1, teamName2, roleId) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_role_teams.test", "teams.#"), + resource.TestCheckResourceAttr("data.github_organization_role_teams.test", "teams.#", "2"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.0.team_id", "github_team.test_1", "id"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.0.slug", "github_team.test_1", "slug"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.0.name", "github_team.test_1", "name"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.1.team_id", "github_team.test_2", "id"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.1.slug", "github_team.test_2", "slug"), + resource.TestCheckResourceAttrPair("data.github_organization_role_teams.test", "teams.1.name", "github_team.test_2", "name"), + ), + }, + }, + }) + }) +} diff --git a/github/data_source_github_organization_role_test.go b/github/data_source_github_organization_role_test.go new file mode 100644 index 0000000000..04558f09ee --- /dev/null +++ b/github/data_source_github_organization_role_test.go @@ -0,0 +1,38 @@ +package github + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceGithubOrganizationRole(t *testing.T) { + t.Run("get the organization role without error", func(t *testing.T) { + roleId := 138 + config := fmt.Sprintf(` + data "github_organization_role" "test" { + role_id = %d + } + `, roleId) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.github_organization_role.test", "role_id", strconv.Itoa(roleId)), + resource.TestCheckResourceAttr("data.github_organization_role.test", "name", "security_manager"), + resource.TestCheckResourceAttr("data.github_organization_role.test", "source", "Predefined"), + resource.TestCheckResourceAttr("data.github_organization_role.test", "base_role", "read"), + resource.TestCheckResourceAttrSet("data.github_organization_role.test", "description"), + resource.TestCheckResourceAttrSet("data.github_organization_role.test", "permissions.#"), + ), + }, + }, + }) + }) +} diff --git a/github/data_source_github_organization_role_users.go b/github/data_source_github_organization_role_users.go new file mode 100644 index 0000000000..ecfe254947 --- /dev/null +++ b/github/data_source_github_organization_role_users.go @@ -0,0 +1,102 @@ +package github + +import ( + "context" + "fmt" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubOrganizationRoleUsers() *schema.Resource { + return &schema.Resource{ + Description: "Lookup all users assigned to a custom organization role.", + + Read: dataSourceGithubOrganizationRoleUsersRead, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization role.", + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "users": { + Description: "Users assigned to the organization role.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "user_id": { + Description: "The ID of the user.", + Type: schema.TypeInt, + Computed: true, + }, + "login": { + Description: "The login for the GitHub user account.", + Type: schema.TypeString, + Computed: true, + }, + // See https://github.com/google/go-github/issues/3364 + // "assignment": { + // Description: "Determines if the team has a direct, indirect, or mixed relationship to a role.", + // Type: schema.TypeString, + // Computed: true, + // }, + // "parent_team_id": { + // Description: "The ID of the parent team if this is an indirect assignment.", + // Type: schema.TypeString, + // Computed: true, + // }, + // "parent_team_slug": { + // Description: "The slug of the parent team if this is an indirect assignment.", + // Type: schema.TypeString, + // Computed: true, + // }, + }, + }, + }, + }, + } +} + +func dataSourceGithubOrganizationRoleUsersRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + + allUsers := make([]any, 0) + + opts := &github.ListOptions{ + PerPage: maxPerPage, + } + + for { + users, resp, err := client.Organizations.ListUsersAssignedToOrgRole(ctx, orgName, roleId, opts) + if err != nil { + return err + } + + for _, user := range users { + u := map[string]any{ + "id": user.GetID(), + "login": user.GetLogin(), + } + allUsers = append(allUsers, u) + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + + d.SetId(fmt.Sprintf("%d", roleId)) + if err := d.Set("users", allUsers); err != nil { + return fmt.Errorf("error setting users: %s", err) + } + + return nil +} diff --git a/github/data_source_github_organization_role_users_test.go b/github/data_source_github_organization_role_users_test.go new file mode 100644 index 0000000000..4350b8e4d5 --- /dev/null +++ b/github/data_source_github_organization_role_users_test.go @@ -0,0 +1,97 @@ +package github + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceGithubOrganizationRoleUsers(t *testing.T) { + login := os.Getenv("GITHUB_IN_ORG_USER") + if len(login) == 0 { + t.Skip("set inOrgUser to unskip this test run") + } + + t.Run("get the organization role users without error", func(t *testing.T) { + roleId := 8134 + config := fmt.Sprintf(` + resource "github_organization_role_user" "test" { + role_id = %d + login = "%s" + } + + data "github_organization_role_users" "test" { + role_id = %[1]d + + depends_on = [ + github_organization_role_user.test + ] + } + `, roleId, login) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_role_users.test", "users.#"), + resource.TestCheckResourceAttr("data.github_organization_role_users.test", "users.#", "1"), + resource.TestCheckResourceAttrSet("data.github_organization_role_users.test", "users.0.user_id"), + resource.TestCheckResourceAttr("data.github_organization_role_users.test", "users.0.login", login), + ), + }, + }, + }) + }) + + t.Run("get indirect organization role users without error", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + teamName := fmt.Sprintf("tf-acc-team-%s", randomID) + roleId := 8134 + config := fmt.Sprintf(` + resource "github_team" "test" { + name = "%s" + privacy = "closed" + } + + resource "github_team_membership" "test" { + team_id = github_team.test.id + username = "%s" + } + + resource "github_organization_role_team" "test" { + role_id = %d + team_slug = github_team.test.slug + } + + data "github_organization_role_users" "test" { + role_id = %[3]d + + depends_on = [ + github_organization_role_team.test + ] + } + `, teamName, login, roleId) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_role_users.test", "users.#"), + resource.TestCheckResourceAttr("data.github_organization_role_users.test", "users.#", "1"), + resource.TestCheckResourceAttrSet("data.github_organization_role_users.test", "users.0.user_id"), + resource.TestCheckResourceAttr("data.github_organization_role_users.test", "users.0.login", login), + ), + }, + }, + }) + }) +} diff --git a/github/data_source_github_organization_roles.go b/github/data_source_github_organization_roles.go new file mode 100644 index 0000000000..dc24318994 --- /dev/null +++ b/github/data_source_github_organization_roles.go @@ -0,0 +1,90 @@ +package github + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubOrganizationRoles() *schema.Resource { + return &schema.Resource{ + Description: "Lookup all custom roles in an organization.", + + Read: dataSourceGithubOrganizationRolesRead, + + Schema: map[string]*schema.Schema{ + "roles": { + Description: "Available organization roles.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization role.", + Type: schema.TypeInt, + Computed: true, + }, + "name": { + Description: "The name of the organization role.", + Type: schema.TypeString, + Computed: true, + }, + "description": { + Description: "The description of the organization role.", + Type: schema.TypeString, + Computed: true, + }, + "source": { + Description: "The source of this role; one of `Predefined`, `Organization`, or `Enterprise`.", + Type: schema.TypeString, + Computed: true, + }, + "base_role": { + Description: "The system role from which this role inherits permissions.", + Type: schema.TypeString, + Computed: true, + }, + "permissions": { + Description: "A list of permissions included in this role.", + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceGithubOrganizationRolesRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + ret, _, err := client.Organizations.ListRoles(ctx, orgName) + if err != nil { + return err + } + + allRoles := make([]any, ret.GetTotalCount()) + for i, role := range ret.CustomRepoRoles { + r := map[string]any{ + "role_id": role.GetID(), + "name": role.GetName(), + "description": role.GetDescription(), + "source": role.GetSource(), + "base_role": role.GetBaseRole(), + "permissions": role.Permissions, + } + allRoles[i] = r + } + + d.SetId(fmt.Sprintf("%s/github-org-roles", orgName)) + if err := d.Set("roles", allRoles); err != nil { + return fmt.Errorf("error setting roles: %s", err) + } + + return nil +} diff --git a/github/data_source_github_organization_roles_test.go b/github/data_source_github_organization_roles_test.go new file mode 100644 index 0000000000..f945e84a30 --- /dev/null +++ b/github/data_source_github_organization_roles_test.go @@ -0,0 +1,28 @@ +package github + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceGithubOrganizationRoles(t *testing.T) { + t.Run("get the organization roles without error", func(t *testing.T) { + config := ` + data "github_organization_roles" "test" {} + ` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_roles.test", "roles.#"), + ), + }, + }, + }) + }) +} diff --git a/github/data_source_github_organization_security_managers.go b/github/data_source_github_organization_security_managers.go new file mode 100644 index 0000000000..3167f6b5d1 --- /dev/null +++ b/github/data_source_github_organization_security_managers.go @@ -0,0 +1,75 @@ +package github + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGithubOrganizationSecurityManagers() *schema.Resource { + return &schema.Resource{ + Read: dataSourceGithubOrganizationSecurityManagersRead, + + Schema: map[string]*schema.Schema{ + "teams": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Description: "Unique identifier of the team.", + Type: schema.TypeInt, + Computed: true, + }, + "slug": { + Description: "Name based identifier of the team.", + Type: schema.TypeString, + Computed: true, + }, + "name": { + Description: "Name of the team.", + Type: schema.TypeString, + Computed: true, + }, + "permission": { + Description: "Permission that the team will have for its repositories.", + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceGithubOrganizationSecurityManagersRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + allTeams := make([]interface{}, 0) + + teams, _, err := client.Organizations.ListSecurityManagerTeams(ctx, orgName) + if err != nil { + return err + } + + for _, team := range teams { + t := map[string]any{ + "id": team.GetID(), + "slug": team.GetSlug(), + "name": team.GetName(), + "permission": team.GetPermission(), + } + allTeams = append(allTeams, t) + } + + d.SetId(fmt.Sprintf("%s/github-org-security-managers", orgName)) + if err := d.Set("teams", allTeams); err != nil { + return fmt.Errorf("error setting teams: %s", err) + } + + return nil +} diff --git a/github/data_source_github_organization_security_managers_test.go b/github/data_source_github_organization_security_managers_test.go new file mode 100644 index 0000000000..ce32902ed0 --- /dev/null +++ b/github/data_source_github_organization_security_managers_test.go @@ -0,0 +1,47 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceGithubOrganizationSecurityManagers(t *testing.T) { + t.Run("get the organization security managers without error", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + teamName := fmt.Sprintf("tf-acc-%s", randomID) + + config := fmt.Sprintf(` + resource "github_team" "test" { + name = "%s" + } + + resource "github_organization_security_manager" "test" { + team_slug = github_team.test.slug + } + + data "github_organization_security_managers" "test" { + depends_on = [ + github_organization_security_manager.test + ] + } + `, teamName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.github_organization_security_managers.test", "teams.#"), + resource.TestCheckResourceAttr("data.github_organization_security_managers.test", "teams.#", "1"), + resource.TestCheckResourceAttr("data.github_organization_security_managers.test", "teams.0.name", teamName), + ), + }, + }, + }) + }) +} diff --git a/github/provider.go b/github/provider.go index a9a04d0474..e865c2c214 100644 --- a/github/provider.go +++ b/github/provider.go @@ -160,8 +160,12 @@ func Provider() *schema.Provider { "github_organization_block": resourceOrganizationBlock(), "github_organization_custom_role": resourceGithubOrganizationCustomRole(), "github_organization_project": resourceGithubOrganizationProject(), - "github_organization_security_manager": resourceGithubOrganizationSecurityManager(), + "github_organization_repository_role": resourceGithubOrganizationRepositoryRole(), + "github_organization_role": resourceGithubOrganizationRole(), + "github_organization_role_team": resourceGithubOrganizationRoleTeam(), + "github_organization_role_user": resourceGithubOrganizationRoleUser(), "github_organization_ruleset": resourceGithubOrganizationRuleset(), + "github_organization_security_manager": resourceGithubOrganizationSecurityManager(), "github_organization_settings": resourceGithubOrganizationSettings(), "github_organization_webhook": resourceGithubOrganizationWebhook(), "github_project_card": resourceGithubProjectCard(), @@ -232,6 +236,13 @@ func Provider() *schema.Provider { "github_organization_custom_role": dataSourceGithubOrganizationCustomRole(), "github_organization_external_identities": dataSourceGithubOrganizationExternalIdentities(), "github_organization_ip_allow_list": dataSourceGithubOrganizationIpAllowList(), + "github_organization_repository_role": dataSourceGithubOrganizationRepositoryRole(), + "github_organization_repository_roles": dataSourceGithubOrganizationRepositoryRoles(), + "github_organization_role": dataSourceGithubOrganizationRole(), + "github_organization_role_teams": dataSourceGithubOrganizationRoleTeams(), + "github_organization_role_users": dataSourceGithubOrganizationRoleUsers(), + "github_organization_roles": dataSourceGithubOrganizationRoles(), + "github_organization_security_managers": dataSourceGithubOrganizationSecurityManagers(), "github_organization_team_sync_groups": dataSourceGithubOrganizationTeamSyncGroups(), "github_organization_teams": dataSourceGithubOrganizationTeams(), "github_organization_webhooks": dataSourceGithubOrganizationWebhooks(), diff --git a/github/resource_github_organization_custom_role.go b/github/resource_github_organization_custom_role.go index 8bed03804d..632e3a5253 100644 --- a/github/resource_github_organization_custom_role.go +++ b/github/resource_github_organization_custom_role.go @@ -12,6 +12,8 @@ import ( func resourceGithubOrganizationCustomRole() *schema.Resource { return &schema.Resource{ + DeprecationMessage: "This resource is deprecated and will be removed in a future release. Use github_organization_repository_role resource instead.", + Create: resourceGithubOrganizationCustomRoleCreate, Read: resourceGithubOrganizationCustomRoleRead, Update: resourceGithubOrganizationCustomRoleUpdate, @@ -70,7 +72,6 @@ func resourceGithubOrganizationCustomRoleCreate(d *schema.ResourceData, meta int BaseRole: github.String(d.Get("base_role").(string)), Permissions: permissionsStr, }) - if err != nil { return fmt.Errorf("error creating GitHub custom repository role %s (%s): %s", orgName, d.Get("name").(string), err) } diff --git a/github/resource_github_organization_repository_role.go b/github/resource_github_organization_repository_role.go new file mode 100644 index 0000000000..9de5299dad --- /dev/null +++ b/github/resource_github_organization_repository_role.go @@ -0,0 +1,210 @@ +package github + +import ( + "context" + "fmt" + "log" + "strconv" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceGithubOrganizationRepositoryRole() *schema.Resource { + return &schema.Resource{ + Description: "Manage a custom organization repository role.", + Create: resourceGithubOrganizationRepositoryRoleCreate, + Read: resourceGithubOrganizationRepositoryRoleRead, + Update: resourceGithubOrganizationRepositoryRoleUpdate, + Delete: resourceGithubOrganizationRepositoryRoleDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization repository role.", + Type: schema.TypeInt, + Computed: true, + }, + "name": { + Description: "The name of the organization repository role.", + Type: schema.TypeString, + Required: true, + }, + "description": { + Description: "The description of the organization repository role.", + Type: schema.TypeString, + Optional: true, + }, + "base_role": { + Description: "The base role for the organization repository role.", + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateValueFunc([]string{"read", "triage", "write", "maintain"}), + }, + "permissions": { + Description: "The permissions for the organization repository role.", + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + MinItems: 1, + }, + }, + } +} + +func resourceGithubOrganizationRepositoryRoleCreate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + orgName := meta.(*Owner).name + ctx := context.Background() + + permissions := d.Get("permissions").(*schema.Set).List() + permissionsStr := make([]string, len(permissions)) + for i, v := range permissions { + permissionsStr[i] = v.(string) + } + + role, _, err := client.Organizations.CreateCustomRepoRole(ctx, orgName, &github.CreateOrUpdateCustomRepoRoleOptions{ + Name: github.String(d.Get("name").(string)), + Description: github.String(d.Get("description").(string)), + BaseRole: github.String(d.Get("base_role").(string)), + Permissions: permissionsStr, + }) + if err != nil { + return fmt.Errorf("error creating GitHub organization repository role (%s/%s): %s", orgName, d.Get("name").(string), err) + } + + d.SetId(fmt.Sprint(role.GetID())) + return resourceGithubOrganizationRoleRead(d, meta) +} + +func resourceGithubOrganizationRepositoryRoleRead(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return err + } + + // TODO: Use this code when go-github adds the functionality to get a custom repo role + // role, _, err := client.Organizations.GetCustomRepoRole(ctx, orgName, roleId) + // if err != nil { + // if ghErr, ok := err.(*github.ErrorResponse); ok { + // if ghErr.Response.StatusCode == http.StatusNotFound { + // log.Printf("[WARN] GitHub organization repository role (%s/%d) not found, removing from state", orgName, roleId) + // d.SetId("") + // return nil + // } + // } + // return err + // } + + roles, _, err := client.Organizations.ListCustomRepoRoles(ctx, orgName) + if err != nil { + return err + } + + var role *github.CustomRepoRoles + for _, r := range roles.CustomRepoRoles { + if r.GetID() == roleId { + role = r + break + } + } + + if role == nil { + log.Printf("[WARN] GitHub organization repository role (%s/%d) not found, removing from state", orgName, roleId) + d.SetId("") + return nil + } + + if err = d.Set("role_id", role.GetID()); err != nil { + return err + } + if err = d.Set("name", role.Name); err != nil { + return err + } + if err = d.Set("description", role.Description); err != nil { + return err + } + if err = d.Set("base_role", role.BaseRole); err != nil { + return err + } + if err = d.Set("permissions", role.Permissions); err != nil { + return err + } + + return nil +} + +func resourceGithubOrganizationRepositoryRoleUpdate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return err + } + + permissions := d.Get("permissions").(*schema.Set).List() + permissionsStr := make([]string, len(permissions)) + for i, v := range permissions { + permissionsStr[i] = v.(string) + } + + update := &github.CreateOrUpdateCustomRepoRoleOptions{ + Name: github.String(d.Get("name").(string)), + Description: github.String(d.Get("description").(string)), + BaseRole: github.String(d.Get("base_role").(string)), + Permissions: permissionsStr, + } + + _, _, err = client.Organizations.UpdateCustomRepoRole(ctx, orgName, roleId, update) + if err != nil { + return fmt.Errorf("error updating GitHub organization repository role (%s/%s): %s", orgName, d.Get("name").(string), err) + } + + return resourceGithubOrganizationRoleRead(d, meta) +} + +func resourceGithubOrganizationRepositoryRoleDelete(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return err + } + + _, err = client.Organizations.DeleteCustomRepoRole(ctx, orgName, roleId) + if err != nil { + return fmt.Errorf("Error deleting GitHub organization repository role %s (%d): %s", orgName, roleId, err) + } + + return nil +} diff --git a/github/resource_github_organization_repository_role_test.go b/github/resource_github_organization_repository_role_test.go new file mode 100644 index 0000000000..885828635b --- /dev/null +++ b/github/resource_github_organization_repository_role_test.go @@ -0,0 +1,86 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubOrganizationRepositoryRole(t *testing.T) { + t.Run("can create an organization repository role without erroring", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + name := fmt.Sprintf("tf-acc-org-repo-role-%s", randomID) + description := "This is a test org repo role." + baseRole := "write" + permission0 := "reopen_issue" + permission1 := "reopen_pull_request" + + config := fmt.Sprintf(` + resource "github_organization_repository_role" "test" { + name = "%s" + description = "%s" + base_role = "%s" + permissions = [ + "%s", + "%s" + ] + } + `, name, description, baseRole, permission0, permission1) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, enterprise) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("github_organization_repository_role.test", "id"), + resource.TestCheckResourceAttrSet("github_organization_repository_role.test", "role_id"), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "name", name), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "description", description), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "base_role", baseRole), + resource.TestCheckResourceAttrSet("github_organization_repository_role.test", "permissions.#"), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "permissions.#", "2"), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "permissions.0", permission0), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "permissions.1", permission1), + ), + }, + }, + }) + }) + + t.Run("can create an minimal organization repository role without erroring", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + name := fmt.Sprintf("tf-acc-org-repo-role-%s", randomID) + permission0 := "reopen_issue" + + config := fmt.Sprintf(` + resource "github_organization_repository_role" "test" { + name = "%s" + permissions = [ + "%s" + ] + } + `, name, permission0) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, enterprise) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("github_organization_repository_role.test", "id"), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "name", name), + resource.TestCheckResourceAttrSet("github_organization_repository_role.test", "base_role"), + resource.TestCheckResourceAttrSet("github_organization_repository_role.test", "permissions.#"), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "permissions.#", "1"), + resource.TestCheckResourceAttr("github_organization_repository_role.test", "permissions.0", permission0), + ), + }, + }, + }) + }) +} diff --git a/github/resource_github_organization_role.go b/github/resource_github_organization_role.go new file mode 100644 index 0000000000..b1bf0ad5da --- /dev/null +++ b/github/resource_github_organization_role.go @@ -0,0 +1,193 @@ +package github + +import ( + "context" + "fmt" + "log" + "net/http" + "strconv" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceGithubOrganizationRole() *schema.Resource { + return &schema.Resource{ + Description: "Manage a custom organization role.", + + Create: resourceGithubOrganizationRoleCreate, + Read: resourceGithubOrganizationRoleRead, + Update: resourceGithubOrganizationRoleUpdate, + Delete: resourceGithubOrganizationRoleDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization role.", + Type: schema.TypeInt, + Computed: true, + }, + "name": { + Description: "The name of the organization role.", + Type: schema.TypeString, + Required: true, + }, + "description": { + Description: "The description of the organization role.", + Type: schema.TypeString, + Optional: true, + }, + "base_role": { + Description: "The base role for the organization role.", + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateDiagFunc: validateValueFunc([]string{"read", "triage", "write", "maintain", "admin"}), + }, + "permissions": { + Description: "The permissions for the organization role.", + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + MinItems: 1, + }, + }, + } +} + +func resourceGithubOrganizationRoleCreate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + orgName := meta.(*Owner).name + ctx := context.Background() + + permissions := d.Get("permissions").(*schema.Set).List() + permissionsStr := make([]string, len(permissions)) + for i, v := range permissions { + permissionsStr[i] = v.(string) + } + + role, _, err := client.Organizations.CreateCustomOrgRole(ctx, orgName, &github.CreateOrUpdateOrgRoleOptions{ + Name: github.String(d.Get("name").(string)), + Description: github.String(d.Get("description").(string)), + BaseRole: github.String(d.Get("base_role").(string)), + Permissions: permissionsStr, + }) + if err != nil { + return fmt.Errorf("error creating GitHub custom organization role (%s/%s): %s", orgName, d.Get("name").(string), err) + } + + d.SetId(fmt.Sprint(role.GetID())) + return resourceGithubOrganizationRoleRead(d, meta) +} + +func resourceGithubOrganizationRoleRead(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return err + } + + role, _, err := client.Organizations.GetOrgRole(ctx, orgName, roleId) + if err != nil { + if ghErr, ok := err.(*github.ErrorResponse); ok { + if ghErr.Response.StatusCode == http.StatusNotFound { + log.Printf("[WARN] GitHub custom organization role (%s/%d) not found, removing from state", orgName, roleId) + d.SetId("") + return nil + } + } + return err + } + + if err = d.Set("role_id", role.GetID()); err != nil { + return err + } + if err = d.Set("name", role.Name); err != nil { + return err + } + if err = d.Set("description", role.Description); err != nil { + return err + } + if err = d.Set("base_role", role.BaseRole); err != nil { + return err + } + if err = d.Set("permissions", role.Permissions); err != nil { + return err + } + + return nil +} + +func resourceGithubOrganizationRoleUpdate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return err + } + + permissions := d.Get("permissions").(*schema.Set).List() + permissionsStr := make([]string, len(permissions)) + for i, v := range permissions { + permissionsStr[i] = v.(string) + } + + update := &github.CreateOrUpdateOrgRoleOptions{ + Name: github.String(d.Get("name").(string)), + Description: github.String(d.Get("description").(string)), + BaseRole: github.String(d.Get("base_role").(string)), + Permissions: permissionsStr, + } + + _, _, err = client.Organizations.UpdateCustomOrgRole(ctx, orgName, roleId, update) + if err != nil { + return fmt.Errorf("error updating GitHub custom organization role (%s/%s): %s", orgName, d.Get("name").(string), err) + } + + return resourceGithubOrganizationRoleRead(d, meta) +} + +func resourceGithubOrganizationRoleDelete(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId, err := strconv.ParseInt(d.Id(), 10, 64) + if err != nil { + return err + } + + _, err = client.Organizations.DeleteCustomOrgRole(ctx, orgName, roleId) + if err != nil { + return fmt.Errorf("Error deleting GitHub custom organization role %s (%d): %s", orgName, roleId, err) + } + + return nil +} diff --git a/github/resource_github_organization_role_team.go b/github/resource_github_organization_role_team.go new file mode 100644 index 0000000000..f5a9baf03d --- /dev/null +++ b/github/resource_github_organization_role_team.go @@ -0,0 +1,138 @@ +package github + +import ( + "context" + "log" + "strconv" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceGithubOrganizationRoleTeam() *schema.Resource { + return &schema.Resource{ + Description: "Manage an association between an organization role and a team.", + + Create: resourceGithubOrganizationRoleTeamCreate, + Read: resourceGithubOrganizationRoleTeamRead, + Delete: resourceGithubOrganizationRoleTeamDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The ID of the organization role.", + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "team_slug": { + Description: "The slug of the team name.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceGithubOrganizationRoleTeamCreate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + teamSlug := d.Get("team_slug").(string) + + _, err = client.Organizations.AssignOrgRoleToTeam(ctx, orgName, teamSlug, roleId) + if err != nil { + return err + } + + d.SetId(buildTwoPartID(strconv.FormatInt(roleId, 10), teamSlug)) + + return resourceGithubOrganizationRoleTeamRead(d, meta) +} + +func resourceGithubOrganizationRoleTeamRead(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleIdString, teamSlug, err := parseTwoPartID(d.Id(), "role_id", "team_slug") + if err != nil { + return err + } + roleId, err := strconv.ParseInt(roleIdString, 10, 64) + if err != nil { + return err + } + + opts := &github.ListOptions{ + PerPage: maxPerPage, + } + + var team *github.Team + for { + teams, resp, err := client.Organizations.ListTeamsAssignedToOrgRole(ctx, orgName, roleId, opts) + if err != nil { + return err + } + + for _, t := range teams { + if t.GetSlug() == teamSlug { + team = t + break + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + + if team == nil { + log.Printf("[INFO] Removing organization role team (%d:%s) from state because it no longer exists in GitHub", roleId, teamSlug) + d.SetId("") + return nil + } + + if err = d.Set("role_id", roleId); err != nil { + return err + } + + if err = d.Set("team_slug", teamSlug); err != nil { + return err + } + + return nil +} + +func resourceGithubOrganizationRoleTeamDelete(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + teamSlug := d.Get("team_slug").(string) + + _, err = client.Organizations.RemoveOrgRoleFromTeam(ctx, orgName, teamSlug, roleId) + return err +} diff --git a/github/resource_github_organization_role_team_test.go b/github/resource_github_organization_role_team_test.go new file mode 100644 index 0000000000..266871b4c0 --- /dev/null +++ b/github/resource_github_organization_role_team_test.go @@ -0,0 +1,42 @@ +package github + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubOrganizationRoleTeam(t *testing.T) { + t.Run("adds team to an organization org role", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + teamName := fmt.Sprintf("tf-acc-team-%s", randomID) + roleId := 8134 + config := fmt.Sprintf(` + resource "github_team" "test" { + name = "%s" + } + + resource "github_organization_role_team" "test" { + role_id = %d + team_slug = github_team.test.slug + } + `, teamName, roleId) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_organization_role_team.test", "role_id", strconv.Itoa(roleId)), + resource.TestCheckResourceAttrPair("github_team.test", "slug", "github_organization_role_team.test", "team_slug"), + ), + }, + }, + }) + }) +} diff --git a/github/resource_github_organization_role_test.go b/github/resource_github_organization_role_test.go new file mode 100644 index 0000000000..55eac10c89 --- /dev/null +++ b/github/resource_github_organization_role_test.go @@ -0,0 +1,85 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubOrganizationRole(t *testing.T) { + t.Run("can create an organization role without erroring", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + name := fmt.Sprintf("tf-acc-org-role-%s", randomID) + description := "This is a test org role." + baseRole := "write" + permission0 := "read_organization_actions_usage_metrics" + permission1 := "read_audit_logs" + config := fmt.Sprintf(` + resource "github_organization_role" "test" { + name = "%s" + description = "%s" + base_role = "%s" + permissions = [ + "%s", + %s + ] + } + `, name, description, baseRole, permission0, permission1) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, enterprise) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("github_organization_role.test", "id"), + resource.TestCheckResourceAttrSet("github_organization_role.test", "role_id"), + resource.TestCheckResourceAttr("github_organization_role.test", "name", name), + resource.TestCheckResourceAttr("github_organization_role.test", "description", description), + resource.TestCheckResourceAttr("github_organization_role.test", "base_role", baseRole), + resource.TestCheckResourceAttrSet("github_organization_role.test", "permissions.#"), + resource.TestCheckResourceAttr("github_organization_role.test", "permissions.#", "2"), + resource.TestCheckResourceAttr("github_organization_role.test", "permissions.0", permission0), + resource.TestCheckResourceAttr("github_organization_role.test", "permissions.1", permission1), + ), + }, + }, + }) + }) + + t.Run("can create an minimal organization role without erroring", func(t *testing.T) { + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + name := fmt.Sprintf("tf-acc-org-role-%s", randomID) + permission0 := "read_organization_actions_usage_metrics" + + config := fmt.Sprintf(` + resource "github_organization_role" "test" { + name = "%s" + permissions = [ + "%s" + ] + } + `, name, permission0) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, enterprise) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("github_organization_role.test", "id"), + resource.TestCheckResourceAttr("github_organization_role.test", "name", name), + resource.TestCheckResourceAttrSet("github_organization_role.test", "base_role"), + resource.TestCheckResourceAttrSet("github_organization_role.test", "permissions.#"), + resource.TestCheckResourceAttr("github_organization_role.test", "permissions.#", "1"), + resource.TestCheckResourceAttr("github_organization_role.test", "permissions.0", permission0), + ), + }, + }, + }) + }) +} diff --git a/github/resource_github_organization_role_user.go b/github/resource_github_organization_role_user.go new file mode 100644 index 0000000000..2339e39f43 --- /dev/null +++ b/github/resource_github_organization_role_user.go @@ -0,0 +1,138 @@ +package github + +import ( + "context" + "log" + "strconv" + + "github.com/google/go-github/v66/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceGithubOrganizationRoleUser() *schema.Resource { + return &schema.Resource{ + Description: "Manage an association between an organization role and a user.", + + Create: resourceGithubOrganizationRoleUserCreate, + Read: resourceGithubOrganizationRoleUserRead, + Delete: resourceGithubOrganizationRoleUserDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "role_id": { + Description: "The unique identifier of the organization role.", + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "login": { + Description: "The login for the GitHub user account.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceGithubOrganizationRoleUserCreate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + login := d.Get("login").(string) + + _, err = client.Organizations.AssignOrgRoleToUser(ctx, orgName, login, roleId) + if err != nil { + return err + } + + d.SetId(buildTwoPartID(strconv.FormatInt(roleId, 10), login)) + + return resourceGithubOrganizationRoleUserRead(d, meta) +} + +func resourceGithubOrganizationRoleUserRead(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleIdString, login, err := parseTwoPartID(d.Id(), "role_id", "login") + if err != nil { + return err + } + roleId, err := strconv.ParseInt(roleIdString, 10, 64) + if err != nil { + return err + } + + opts := &github.ListOptions{ + PerPage: maxPerPage, + } + + var user *github.User + for { + users, resp, err := client.Organizations.ListUsersAssignedToOrgRole(ctx, orgName, roleId, opts) + if err != nil { + return err + } + + for _, u := range users { + if u.GetLogin() == login { + user = u + break + } + } + + if resp.NextPage == 0 { + break + } + opts.Page = resp.NextPage + } + + if user == nil { + log.Printf("[INFO] Removing organization role User (%d:%s) from state because it no longer exists in GitHub", roleId, login) + d.SetId("") + return nil + } + + if err = d.Set("role_id", roleId); err != nil { + return err + } + + if err = d.Set("login", login); err != nil { + return err + } + + return nil +} + +func resourceGithubOrganizationRoleUserDelete(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v3client + ctx := context.Background() + orgName := meta.(*Owner).name + + roleId := int64(d.Get("role_id").(int)) + login := d.Get("login").(string) + + _, err = client.Organizations.RemoveOrgRoleFromUser(ctx, orgName, login, roleId) + return err +} diff --git a/github/resource_github_organization_role_user_test.go b/github/resource_github_organization_role_user_test.go new file mode 100644 index 0000000000..072c470d62 --- /dev/null +++ b/github/resource_github_organization_role_user_test.go @@ -0,0 +1,41 @@ +package github + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubOrganizationRoleUser(t *testing.T) { + t.Run("adds user to an organization org role", func(t *testing.T) { + login := os.Getenv("GITHUB_IN_ORG_USER") + if len(login) == 0 { + t.Skip("set inOrgUser to unskip this test run") + } + + roleId := 8134 + config := fmt.Sprintf(` + resource "github_organization_role_user" "test" { + role_id = %d + login = "%s" + } + `, roleId, login) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, organization) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_organization_role_user.test", "role_id", strconv.Itoa(roleId)), + resource.TestCheckResourceAttr("github_organization_role_user.test", "login", login), + ), + }, + }, + }) + }) +} diff --git a/github/resource_github_organization_security_manager_test.go b/github/resource_github_organization_security_manager_test.go index 070ad9ae4d..5fec6a7486 100644 --- a/github/resource_github_organization_security_manager_test.go +++ b/github/resource_github_organization_security_manager_test.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -func TestAccGithubOrganizationSecurityManagers(t *testing.T) { +func TestAccGithubOrganizationSecurityManager(t *testing.T) { randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) t.Run("adds team as security manager", func(t *testing.T) { diff --git a/website/docs/d/organization_custom_role.html.markdown b/website/docs/d/organization_custom_role.html.markdown index 0fa51ac50d..b9ed7563df 100644 --- a/website/docs/d/organization_custom_role.html.markdown +++ b/website/docs/d/organization_custom_role.html.markdown @@ -7,6 +7,8 @@ description: |- # github\_organization\_custom\_role +~> **Note:** This data source is deprecated, please use the `github_organizationrepository_role` data source instead. + Use this data source to retrieve information about a custom role in a GitHub Organization. ~> Note: Custom roles are currently only available in GitHub Enterprise Cloud. diff --git a/website/docs/d/organization_repository_role.html.markdown b/website/docs/d/organization_repository_role.html.markdown new file mode 100644 index 0000000000..1ef13f6d08 --- /dev/null +++ b/website/docs/d/organization_repository_role.html.markdown @@ -0,0 +1,33 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_repository_role Data Source" +description: |- + Lookup a custom organization repository role. +--- + +# github_organization_repository_role (Data Source) + +Lookup a custom organization repository role. + +~> **Note**: Custom organization repository roles are currently only available in GitHub Enterprise Cloud. + +## Example Usage + +```terraform +data "github_organization_repository_role" "example" { + role_id = 1234 +} +``` + +## Schema + +### Required + +- `role_id` (Number) The ID of the organization repository role. + +### Read-Only + +- `name` (String) The name of the organization repository role. +- `description` (String) The description of the organization repository role. +- `base_role` (String) The system role from which this role inherits permissions. +- `permissions` (Set of String) The permissions included in this role. diff --git a/website/docs/d/organization_repository_roles.html.markdown b/website/docs/d/organization_repository_roles.html.markdown new file mode 100644 index 0000000000..2b65549adb --- /dev/null +++ b/website/docs/d/organization_repository_roles.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_repository_roles Data Source" +description: |- + Lookup all custom repository roles in an organization. +--- + +# github_organization_repository_roles (Data Source) + +Lookup all custom repository roles in an organization. + +~> **Note**: Custom organization repository roles are currently only available in GitHub Enterprise Cloud. + +## Example Usage + +```terraform +data "github_organization_repository_roles" "example" { +} +``` + +## Schema + +### Read-Only + +- `roles` (Set of Object, see [schema](#nested-schema-for-roles)) Available organization repository roles. + +## Nested Schema for `roles` + +### Read-Only + +- `role_id` (Number) The ID of the organization repository role. +- `name` (String) The name of the organization repository role. +- `description` (String) The description of the organization repository role. +- `base_role` (String) The system role from which this role inherits permissions. +- `permissions` (Set of String) The permissions included in this role. diff --git a/website/docs/d/organization_role.html.markdown b/website/docs/d/organization_role.html.markdown new file mode 100644 index 0000000000..a6990af53d --- /dev/null +++ b/website/docs/d/organization_role.html.markdown @@ -0,0 +1,32 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_role Data Source" +description: |- + Lookup a custom organization role. +--- + +# github_organization_role (Data Source) + +Lookup a custom organization role. + +## Example Usage + +```terraform +data "github_organization_role" "example" { + role_id = 1234 +} +``` + +## Schema + +### Required + +- `role_id` (Number) The ID of the organization role. + +### Read-Only + +- `name` (String) The name of the organization role. +- `description` (String) The description of the organization role. +- `source` (String) The source of this role; one of `Predefined`, `Organization`, or `Enterprise`. +- `base_role` (String) The system role from which this role inherits permissions. +- `permissions` (Set of String) The permissions included in this role. diff --git a/website/docs/d/organization_role_teams.html.markdown b/website/docs/d/organization_role_teams.html.markdown new file mode 100644 index 0000000000..6dfe676109 --- /dev/null +++ b/website/docs/d/organization_role_teams.html.markdown @@ -0,0 +1,37 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_role_teams Data Source" +description: |- + Lookup all teams assigned to a custom organization role. +--- + +# github_organization_role_teams (Data Source) + +Lookup all teams assigned to a custom organization role. + +## Example Usage + +```terraform +data "github_organization_role_teams" "example" { + role_id = 1234 +} +``` + +## Schema + +### Required + +- `role_id` (Number) The ID of the organization role. + +### Read-Only + +- `teams` (Set of Object, see [schema](#nested-schema-for-teams)) Teams assigned to the organization role. + +## Nested Schema for `teams` + +### Read-Only + +- `team_id` (Number) The ID of the team. +- `slug` (String) The Slug of the team name. +- `name` (String) The name of the team. +- `permission` (String) The permission that the team will have for its repositories. diff --git a/website/docs/d/organization_role_users.html.markdown b/website/docs/d/organization_role_users.html.markdown new file mode 100644 index 0000000000..6ff6d46668 --- /dev/null +++ b/website/docs/d/organization_role_users.html.markdown @@ -0,0 +1,35 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_role_users Data Source" +description: |- + Lookup all users assigned to a custom organization role. +--- + +# github_organization_role_users (Data Source) + +Lookup all users assigned to a custom organization role. + +## Example Usage + +```terraform +data "github_organization_role_users" "example" { + role_id = 1234 +} +``` + +## Schema + +### Required + +- `role_id` (Number) The ID of the organization role. + +### Read-Only + +- `users` (Set of Object, see [schema](#nested-schema-for-users)) Users assigned to the organization role. + +## Nested Schema for `users` + +### Read-Only + +- `user_id` (Number) The ID of the user. +- `login` (String) The login for the GitHub user account. diff --git a/website/docs/d/organization_roles.html.markdown b/website/docs/d/organization_roles.html.markdown new file mode 100644 index 0000000000..78890f6d49 --- /dev/null +++ b/website/docs/d/organization_roles.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_roles Data Source" +description: |- + Lookup all custom roles in an organization. +--- + +# github_organization_roles (Data Source) + +Lookup all custom roles in an organization. + +## Example Usage + +```terraform +data "github_organization_roles" "example" { +} +``` + +## Schema + +### Read-Only + +- `roles` (Set of Object, see [schema](#nested-schema-for-roles)) Available organization roles. + +## Nested Schema for `roles` + +### Read-Only + +- `role_id` (Number) The ID of the organization role. +- `name` (String) The name of the organization role. +- `description` (String) The description of the organization role. +- `source` (String) The source of this role; one of `Predefined`, `Organization`, or `Enterprise`. +- `base_role` (String) The system role from which this role inherits permissions. +- `permissions` (Set of String) The permissions included in this role. diff --git a/website/docs/d/organization_security_managers.html.markdown b/website/docs/d/organization_security_managers.html.markdown new file mode 100644 index 0000000000..3ebea2770d --- /dev/null +++ b/website/docs/d/organization_security_managers.html.markdown @@ -0,0 +1,29 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_security_managers" +description: |- + Get the security managers for an organization. +--- + +# github_organization_security_managers + +Use this data source to retrieve the security managers for an organization. + +## Example Usage + +```hcl +data "github_organization_security_managers" "test" {} +``` + +## Attributes Reference + + * `teams` - An list of GitHub teams. Each `team` block consists of the fields documented below. + +___ + +The `team` block consists of: + + * `id` - Unique identifier of the team. + * `slug` - Name based identifier of the team. + * `name` - Name of the team. + * `permission` - Permission that the team will have for its repositories. diff --git a/website/docs/r/organization_custom_role.html.markdown b/website/docs/r/organization_custom_role.html.markdown index 683009c741..d2ae89d5d8 100644 --- a/website/docs/r/organization_custom_role.html.markdown +++ b/website/docs/r/organization_custom_role.html.markdown @@ -7,7 +7,9 @@ description: |- # github\_organization\_custom\_role -This resource allows you to create and manage custom roles in a GitHub Organization for use in repositories. +~> **Note:** This resource is deprecated, please use the `github_organizationrepository_role` resource instead. + +This resource allows you to create and manage custom roles in a GitHub Organization for use in repositories. ~> Note: Custom roles are currently only available in GitHub Enterprise Cloud. diff --git a/website/docs/r/organization_repository_role.html.markdown b/website/docs/r/organization_repository_role.html.markdown new file mode 100644 index 0000000000..4faa56d1a8 --- /dev/null +++ b/website/docs/r/organization_repository_role.html.markdown @@ -0,0 +1,50 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_repository_role Resource" +description: |- + Manage a custom organization repository role. +--- + +# github_organization_repository_role (Resource) + +Manage a custom organization repository role. + +~> **Note**: Custom organization repository roles are currently only available in GitHub Enterprise Cloud. + +## Example Usage + +```terraform +resource "github_organization_repository_role" "example" { + name = "example" + base_role = "read" + + permissions = [ + "add_assignee", + "add_label" + ] +} +``` + +## Schema + +### Required + +- `name` (String) The name of the organization repository role. +- `base_role` (String) The system role from which this role inherits permissions. +- `permissions` (Set of String, Min: 1) The permissions included in this role. + +### Optional + +- `description` (String) The description of the organization repository role. + +### Read-Only + +- `role_id` (Number) The ID of the organization repository role. + +## Import + +A custom organization repository role can be imported using its ID. + +```shell +terraform import github_organization_repository_role.example 1234 +``` diff --git a/website/docs/r/organization_role.html.markdown b/website/docs/r/organization_role.html.markdown new file mode 100644 index 0000000000..b28eb728a0 --- /dev/null +++ b/website/docs/r/organization_role.html.markdown @@ -0,0 +1,50 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_role Resource" +description: |- + Manage a custom organization role. +--- + +# github_organization_role (Resource) + +Manage a custom organization role. + +~> **Note**: Custom organization roles are currently only available in GitHub Enterprise Cloud. + +## Example Usage + +```terraform +resource "github_organization_role" "example" { + name = "example" + base_role = "read" + + permissions = [ + "read_organization_custom_org_role", + "read_organization_custom_repo_role" + ] +} +``` + +## Schema + +### Required + +- `name` (String) The name of the organization role. +- `permissions` (Set of String, Min: 1) The permissions included in this role. + +### Optional + +- `description` (String) The description of the organization role. +- `base_role` (String) The system role from which this role inherits permissions. + +### Read-Only + +- `role_id` (Number) The ID of the organization role. + +## Import + +A custom organization role can be imported using its ID. + +```shell +terraform import github_organization_role.example 1234 +``` diff --git a/website/docs/r/organization_role_team.html.markdown b/website/docs/r/organization_role_team.html.markdown new file mode 100644 index 0000000000..5518bb19bb --- /dev/null +++ b/website/docs/r/organization_role_team.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_role_team Resource" +description: |- + Manage an association between an organization role and a team. +--- + +# github_organization_role_team (Resource) + +Manage an association between an organization role and a team. + +## Example Usage + +```terraform +resource "github_organization_role_team" "example" { + role_id = 1234 + team_slug = "example-team" +} +``` + +## Schema + +### Required + +- `role_id` (Number) The ID of the organization role. +- `team_slug` (String) The slug of the team name. + +## Import + +An organization role team association can be imported using the role ID and the team slug separated by a `:`. + +```shell +terraform import github_organization_role_team.example "1234:example-team" +``` diff --git a/website/docs/r/organization_role_user.html.markdown b/website/docs/r/organization_role_user.html.markdown new file mode 100644 index 0000000000..c2a92c8a87 --- /dev/null +++ b/website/docs/r/organization_role_user.html.markdown @@ -0,0 +1,34 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_role_user Resource" +description: |- + Manage an association between an organization role and a user. +--- + +# github_organization_role_user (Resource) + +Manage an association between an organization role and a user. + +## Example Usage + +```terraform +resource "github_organization_role_user" "example" { + role_id = 1234 + login = "example-user" +} +``` + +## Schema + +### Required + +- `role_id` (Number) The ID of the organization role. +- `login` (String) The login for the GitHub user account. + +## Import + +An organization role user association can be imported using the role ID and the user login separated by a `:`. + +```shell +terraform import github_organization_role_team.example "1234:example-user" +``` diff --git a/website/github.erb b/website/github.erb index 62cac71347..35ce675c7e 100644 --- a/website/github.erb +++ b/website/github.erb @@ -121,6 +121,27 @@
  • github_organization_ip_allow_list
  • +
  • + organization_repository_role +
  • +
  • + organization_repository_roles +
  • +
  • + organization_role +
  • +
  • + organization_role_teams +
  • +
  • + organization_role_users +
  • +
  • + organization_roles +
  • +
  • + github_organization_security_managers +
  • github_organization_team_sync_groups
  • @@ -283,6 +304,18 @@
  • github_organization_project
  • +
  • + organization_repository_role +
  • +
  • + organization_role +
  • +
  • + organization_role_team +
  • +
  • + organization_role_user +
  • github_organization_ruleset
  • @@ -335,7 +368,7 @@ github_repository_environment_secret
  • - github_repository_environment_variable + github_repository_environment_variable
  • github_repository_file