Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2091fd0
add octokit sdk client
Jul 12, 2024
d612d52
add github_team_org_role_assignment resource
Jul 16, 2024
0942afc
add tests for github_team_org_role_assignment
Jul 16, 2024
dddd5f6
formatting
Jul 16, 2024
e304177
use go-github for listing all team role assignments for a role
Jul 17, 2024
1004bf1
implement proper import logic
Jul 17, 2024
df99246
add docs
Jul 17, 2024
1e94354
test adding .gitattributes to make vendor pr changes not slow down th…
Jul 17, 2024
73bacd9
test adding .gitattributes to make vendor pr changes not slow down th…
Jul 17, 2024
2b91eca
test adding .gitattributes to make vendor pr changes not slow down th…
Jul 17, 2024
826d49a
remove .gitattributes, seems like it had no effect
Jul 17, 2024
5442846
add break iteration over teams assigned to organization role
Jul 17, 2024
4653863
Merge remote-tracking branch 'origin/main' into github_team_organizat…
felixlut Nov 27, 2024
02b0cca
org role team assignment no longer use go-sdk
felixlut Nov 27, 2024
a13f0b2
purge go-sdk completely
felixlut Nov 27, 2024
4f55b6c
Merge branch 'main' into github_team_organization_role_assignment
felixlut Apr 7, 2025
7ceddb7
support either role_name or role_id
felixlut Apr 8, 2025
525de6a
update docs
felixlut Apr 8, 2025
8135e6b
resources should follow convention from gh api (org role before team)
felixlut Apr 8, 2025
805e6ca
update docs
felixlut Apr 8, 2025
9caf4b4
finalize the renaming
felixlut Apr 8, 2025
28c69ef
fix importer
felixlut Apr 8, 2025
87ef13d
go back to only allowing github_slug and role_id
felixlut Apr 8, 2025
e9a1721
linting fix
felixlut Apr 8, 2025
4d732e7
revert go.mod changes
felixlut Apr 8, 2025
1dfba89
Merge branch 'main' into github_team_organization_role_assignment
nickfloyd Oct 20, 2025
79c14f4
Merge branch 'main' into github_team_organization_role_assignment
nickfloyd Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
33 changes: 33 additions & 0 deletions github/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/google/go-github/v62/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
"github.com/octokit/go-sdk/pkg"
"github.com/shurcooL/githubv4"
"golang.org/x/oauth2"
)
Expand All @@ -32,6 +33,7 @@ type Owner struct {
id int64
v3client *github.Client
v4client *githubv4.Client
octokitClient *pkg.Client
StopContext context.Context
IsOrganization bool
}
Expand Down Expand Up @@ -110,6 +112,31 @@ func (c *Config) NewRESTClient(client *http.Client) (*github.Client, error) {
return v3client, nil
}

func (c *Config) NewOctokitClient() (*pkg.Client, error) {

uv3, err := url.Parse(c.BaseURL)
if err != nil {
return nil, err
}

if uv3.String() != "https://api.github.com/" {
uv3.Path = uv3.Path + "api/v3/"
}

octokitClient, err := pkg.NewApiClient(
// pkg.WithUserAgent("my-user-agent"), // Should this be set to terraform-provider-github or similar? Doesn't look like the user-agent is set for the other clients
pkg.WithRequestTimeout(5*time.Second),
pkg.WithBaseUrl(strings.TrimSuffix(uv3.Path, "/")), // the octokit/go-sdk expects the url to not end with a "/"
pkg.WithTokenAuthentication(c.Token),
)

if err != nil {
return nil, err
}

return octokitClient, nil
}

func (c *Config) ConfigureOwner(owner *Owner) (*Owner, error) {
ctx := context.Background()
owner.name = c.Owner
Expand Down Expand Up @@ -152,6 +179,11 @@ func (c *Config) Meta() (interface{}, error) {
return nil, err
}

octokitClient, err := c.NewOctokitClient()
if err != nil {
return nil, err
}

v4client, err := c.NewGraphQLClient(client)
if err != nil {
return nil, err
Expand All @@ -160,6 +192,7 @@ func (c *Config) Meta() (interface{}, error) {
var owner Owner
owner.v4client = v4client
owner.v3client = v3client
owner.octokitClient = octokitClient
owner.StopContext = context.Background()

_, err = c.ConfigureOwner(&owner)
Expand Down
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ func Provider() *schema.Provider {
"github_team": resourceGithubTeam(),
"github_team_members": resourceGithubTeamMembers(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_organization_role_assignment": resourceGithubTeamOrganizationRoleAssignment(),
"github_team_repository": resourceGithubTeamRepository(),
"github_team_settings": resourceGithubTeamSettings(),
"github_team_sync_group_mapping": resourceGithubTeamSyncGroupMapping(),
Expand Down
197 changes: 197 additions & 0 deletions github/resource_github_team_organization_role_assignment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package github

import (
"context"
"log"
"strconv"

"github.com/google/go-github/v62/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
abs "github.com/microsoft/kiota-abstractions-go"
)

func resourceGithubTeamOrganizationRoleAssignment() *schema.Resource {
return &schema.Resource{
Create: resourceGithubTeamOrganizationRoleAssignmentCreate,
Read: resourceGithubTeamOrganizationRoleAssignmentRead,
Delete: resourceGithubTeamOrganizationRoleAssignmentDelete,
Importer: &schema.ResourceImporter{
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
teamIdString, roleID, err := parseTwoPartID(d.Id(), "team_id", "role_id")
if err != nil {
return nil, err
}

teamSlug, err := getTeamSlug(teamIdString, meta)
if err != nil {
return nil, err
}

d.SetId(buildTwoPartID(teamSlug, roleID))
return []*schema.ResourceData{d}, nil
},
},

Schema: map[string]*schema.Schema{
"team_id": {
Type: schema.TypeString,
Required: true,
Description: "The GitHub team id or the GitHub team slug.",
ForceNew: true,
},
"role_id": {
Type: schema.TypeString,
Required: true,
Description: "The GitHub Organization Role id.",
ForceNew: true,
},
},
}
}

func newOctokitClientDefaultRequestConfig() *abs.RequestConfiguration[abs.DefaultQueryParameters] {
headers := abs.NewRequestHeaders()
_ = headers.TryAdd("Accept", "application/vnd.github.v3+json")
_ = headers.TryAdd("X-GitHub-Api-Version", "2022-11-28")

return &abs.RequestConfiguration[abs.DefaultQueryParameters]{
QueryParameters: &abs.DefaultQueryParameters{},
Headers: headers,
}
}

func resourceGithubTeamOrganizationRoleAssignmentCreate(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}

octokitClient := meta.(*Owner).octokitClient

orgName := meta.(*Owner).name
ctx := context.Background()

// The given team id could be an id or a slug
givenTeamId := d.Get("team_id").(string)
teamSlug, err := getTeamSlug(givenTeamId, meta)
if err != nil {
return err
}

roleIDString := d.Get("role_id").(string)
roleID, err := strconv.ParseInt(roleIDString, 10, 32)

if err != nil {
return err
}

defaultRequestConfig := newOctokitClientDefaultRequestConfig()
err = octokitClient.Orgs().ByOrg(orgName).OrganizationRoles().Teams().ByTeam_slug(teamSlug).ByRole_id(int32(roleID)).Put(ctx, defaultRequestConfig)
if err != nil {
return err
}

d.SetId(buildTwoPartID(teamSlug, roleIDString))
return resourceGithubTeamOrganizationRoleAssignmentRead(d, meta)
}

func resourceGithubTeamOrganizationRoleAssignmentRead(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}

restClient := meta.(*Owner).v3client

ctx := context.Background()
orgName := meta.(*Owner).name

teamIdString, roleIDString, err := parseTwoPartID(d.Id(), "team_id", "role_id")
if err != nil {
return err
}

// The given team id could be an id or a slug
teamSlug, err := getTeamSlug(teamIdString, meta)
if err != nil {
return err
}

roleID, err := strconv.ParseInt(roleIDString, 10, 32)
if err != nil {
return err
}

// There is no api for checking a specific team role assignment, so instead we iterate over all teams assigned to the role
// go-github pagination (https://github.com/google/go-github?tab=readme-ov-file#pagination)
options := &github.ListOptions{
PerPage: 100,
}
var foundTeam *github.Team
for {
teams, resp, err := restClient.Organizations.ListTeamsAssignedToOrgRole(ctx, orgName, roleID, options)
if err != nil {
return err
}

for _, team := range teams {
if team.GetSlug() == teamSlug {
foundTeam = team
}

}

if resp.NextPage == 0 {
break
}
options.Page = resp.NextPage
}

if foundTeam == nil {
log.Printf("[WARN] Removing team organization role association %s from state because it no longer exists in GitHub", d.Id())
d.SetId("")
return nil
}

return nil
}

func resourceGithubTeamOrganizationRoleAssignmentDelete(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}

octokitClient := meta.(*Owner).octokitClient

orgName := meta.(*Owner).name
ctx := context.Background()

teamIdString, roleIDString, err := parseTwoPartID(d.Id(), "team_id", "role_id")
if err != nil {
return err
}

// The given team id could be an id or a slug
teamSlug, err := getTeamSlug(teamIdString, meta)
if err != nil {
return err
}

roleID, err := strconv.ParseInt(roleIDString, 10, 32)
if err != nil {
return err
}

if err != nil {
return err
}

defaultRequestConfig := newOctokitClientDefaultRequestConfig()
err = octokitClient.Orgs().ByOrg(orgName).OrganizationRoles().Teams().ByTeam_slug(teamSlug).ByRole_id(int32(roleID)).Delete(ctx, defaultRequestConfig)
if err != nil {
return err
}

return nil
}
Loading