Skip to content

Teams and access

Access to the repositories of an organization results from the base permission of the organization, the teams and their permissions on repositories, and individual collaborators. Otterdog manages the first two; individual collaborators cannot be managed with otterdog.

Base permission

The organization setting default_repository_permission grants a permission on all repositories to every member of the organization: none, read, write or admin, see Setting base permissions for an organization. Members keep higher permissions that they get from a team.

orgs.newOrg('my-project', 'my-org') {
  settings+: {
    default_repository_permission: "none",
  },
}

Open in the Playground

Organization policy

With none, every access must be granted explicitly through a team, which makes access reviewable in the configuration. With read, all members can read private repositories, which eases collaboration in organizations whose members all work on the same projects. A base permission of write or admin is rarely appropriate, as it grants access to every new repository.

Related settings restrict what members can do on organization level, e.g. members_can_create_public_repositories, members_can_create_teams or members_can_delete_repositories, see Organization Settings.

Teams

Teams are defined with orgs.newTeam. Their members can be managed in the configuration or elsewhere:

Members managed ... Configuration
in the configuration members lists the GitHub logins
elsewhere, e.g. in the GitHub web interface skip_members: true, otterdog manages the team but ignores its members
by an external tool, including the team itself the team is excluded with exclude_teams, see Organization-wide policies
by an identity provider (IdP), with GitHub Enterprise Cloud team_sync or external_group_id connects the team to IdP groups, see Teams managed by an identity provider
orgs.newOrg('my-project', 'my-org') {
  teams+: [
    orgs.newTeam('maintainers') {
      description: "Maintainers of the project",
      members+: [
        "alice",
        "bob",
      ],
    },
    orgs.newTeam('contributors') {
      description: "Contributors, managed in the GitHub web interface",
      skip_members: true,
    },
  ],
}

Open in the Playground

Note

Adding a user who is not yet a member of the organization to a team invites this user to the organization. With skip_non_organization_members: true, validate instead reports such users as an error, so that invitations stay under the control of another process.

Teams with privacy: "secret" are only visible to their members and organization owners. Secret teams cannot be used as bypass actors of rulesets, see Creating rulesets for a repository.

Teams managed by an identity provider

On GitHub Enterprise Cloud, the members of a team can be provisioned by groups of an identity provider (IdP), depending on how the organization authenticates its members:

  • with team synchronization, up to five IdP groups, identified by their id, are connected to a team with team_sync
  • with Enterprise Managed Users (EMU), a single external group is connected to a team with external_group_id

The members of such a team are provisioned by the IdP, so members is not set and the current members of the team are ignored, see Identity provider integration.

orgs.newOrg('my-project', 'my-org') {
  teams+: [
    orgs.newTeam('admins') {
      description: "Admins, provisioned by the IdP groups",
      team_sync: [
        orgs.newTeamSync('5d4b0a8e-2c19-4b5f-8d6c-1f2e3a4b5c6d') {
          name: "git-admins",
          description: "Admin access via the IdP",
        },
        orgs.newTeamSync('9f8e7d6c-5b4a-4321-8fed-cba987654321') {
          name: "git-release-managers",
        },
      ],
    },
  ],
}

Open in the Playground

orgs.newOrg('my-project', 'my-org') {
  teams+: [
    orgs.newTeam('admins') {
      description: "Admins, provisioned by an external group",
      external_group_id: 1234,
    },
  ],
}

Open in the Playground

The ids of the available groups can be listed with the GitHub REST API, see List IdP groups for an organization and List external groups available to an organization.

Grant teams access to repositories

The repository property team_permissions maps the slug of a team to its permission on the repository:

orgs.newOrg('my-project', 'my-org') {
  teams+: [
    orgs.newTeam('maintainers'),
    orgs.newTeam('documentation'),
  ],
  _repositories+:: [
    orgs.newRepo('my-repo') {
      team_permissions+: {
        maintainers: "maintain",
        documentation: "triage",
      },
    },
  ],
}

Open in the Playground

The permissions and their GitHub roles are listed in Team Permissions, see also Repository roles for an organization.

Warning

When the default configuration defines team_permissions, as the example default configuration does, the team permissions of every repository are managed: a team that has access to a repository on GitHub but is not listed in its configuration loses its access the next time the configuration is applied. As the removal is a modification of the repository, it is applied even without --delete-resources. This also applies to teams whose members are managed outside otterdog. Review such removals with plan before applying.

The permissions of teams excluded with exclude_teams are ignored, unless the repository lists the team in team_permissions, see Organization-wide policies.

Recommendation

Grant the lowest permission that fits the role of a team, e.g. maintain instead of admin for maintainers, and grant access through teams rather than to individual users, so that access can be reviewed in one place.

Security managers and custom roles

  • The organization setting security_managers assigns the security manager role to teams, see Repository security features.
  • Custom organization roles can be defined with orgs.newOrgRole. They require the enterprise plan.