Skip to content

Conversation

@atilsensalduz
Copy link

@atilsensalduz atilsensalduz commented Feb 23, 2025

This PR introduces a new data source, github_app_installations, to enable listing all installed GitHub Apps within an organization. This addition enhances integration with existing resources such as github_app_installation_repository, providing better automation and management capabilities for GitHub App permissions.

What's New?
New Data Source: github_app_installations
Allows fetching all installed GitHub Apps in an organization:

data "github_organization_app_installations" "all_apps" {}

Example Use Case:
This data source can be integrated with the github_app_installation_repository resource to manage app permissions on specific repositories:

# Local value to find the correct app installation by slug
locals {
  # Find the index of the desired app by its slug
  app_index = index(
    data.github_organization_app_installations.all_apps.installations[*].slug,
    "desired-app-name"  # Replace with your actual app slug
  )
  
  # Get the app_id using the found index
  app_installation_id = data.github_organization_app_installations.all_apps.installations[local.app_index].id
}

# Link the repository to the app installation
resource "github_app_installation_repository" "some_app_repo" {
  installation_id = local.app_installation_id
  repository     = github_repository.some_repo.name
}

API Reference: https://docs.github.com/en/rest/orgs/orgs?apiVersion=2022-11-28#list-app-installations-for-an-organization

Related Issue: #2570

@atilsensalduz atilsensalduz changed the title feat: Add data sources for listing GitHub App installations in an organization feat: add data sources for listing GitHub App installations in an organization Feb 23, 2025
@nickfloyd nickfloyd moved this from 🆕 Triage to 👀 In review in 🧰 Octokit Active Feb 27, 2025
@github-actions
Copy link

github-actions bot commented Dec 6, 2025

👋 Hey Friends, this pull request has been automatically marked as stale because it has no recent activity. It will be closed if no further activity occurs. Please add the Status: Pinned label if you feel that this issue needs to remain open/active. Thank you for your contributions and help in keeping things tidy!

@github-actions github-actions bot added the Status: Stale Used by stalebot to clean house label Dec 6, 2025
Copy link
Collaborator

@deiga deiga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey 👋

Thank you for your contribution!

I've requested a few changes to make this fit into the provider even better :)

Comment on lines 11 to 85
return &schema.Resource{
Read: dataSourceGithubOrganizationAppInstallationsRead,

Schema: map[string]*schema.Schema{
"installations": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeInt,
Computed: true,
},
"slug": {
Type: schema.TypeString,
Computed: true,
},
"app_id": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Could you please add Description fields to the Schema and all fields?


func dataSourceGithubOrganizationAppInstallations() *schema.Resource {
return &schema.Resource{
Read: dataSourceGithubOrganizationAppInstallationsRead,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Could you refactor this to use ReadContext?

import (
"context"

"github.com/google/go-github/v66/github"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I doubt we'll be able to land this before #2602

Maybe you could even rebase on top of that branch?

Suggested change
"github.com/google/go-github/v66/github"
"github.com/google/go-github/v77/github"

"app_id": {
Type: schema.TypeInt,
Computed: true,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Could you add the following fields to this:

  • repository_selection
  • permissions
  • events
  • html_url
  • client_id
  • target_id
  • target_type
  • suspended

@github-project-automation github-project-automation bot moved this from 👀 In review to 🏗 In progress in 🧰 Octokit Active Dec 10, 2025
@deiga
Copy link
Collaborator

deiga commented Jan 15, 2026

@atilsensalduz Please rebase and go through the comments

@atilsensalduz
Copy link
Author

@atilsensalduz Please rebase and go through the comments

Hey @deiga , apologies for the late reply. I somehow missed this PR. Thanks so much for the review! I’ve worked through your suggestions.

@atilsensalduz
Copy link
Author

Hi @deiga , I've changed the PR according to your suggestions. Could you please take a look again when you have time? Thanks in advance.

@atilsensalduz atilsensalduz requested a review from deiga January 16, 2026 11:41
return &schema.Resource{
ReadContext: dataSourceGithubOrganizationAppInstallationsRead,

Schema: map[string]*schema.Schema{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a top-level Description field

client := meta.(*Owner).v3client

options := &github.ListOptions{
PerPage: 100,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets use the global constant here

Suggested change
PerPage: 100,
PerPage: maxPerPage,

Comment on lines 146 to 235
if v := appInstallation.Permissions.Actions; v != nil {
permissions["actions"] = *v
}
if v := appInstallation.Permissions.Administration; v != nil {
permissions["administration"] = *v
}
if v := appInstallation.Permissions.Checks; v != nil {
permissions["checks"] = *v
}
if v := appInstallation.Permissions.Contents; v != nil {
permissions["contents"] = *v
}
if v := appInstallation.Permissions.Deployments; v != nil {
permissions["deployments"] = *v
}
if v := appInstallation.Permissions.Environments; v != nil {
permissions["environments"] = *v
}
if v := appInstallation.Permissions.Issues; v != nil {
permissions["issues"] = *v
}
if v := appInstallation.Permissions.Metadata; v != nil {
permissions["metadata"] = *v
}
if v := appInstallation.Permissions.Members; v != nil {
permissions["members"] = *v
}
if v := appInstallation.Permissions.OrganizationAdministration; v != nil {
permissions["organization_administration"] = *v
}
if v := appInstallation.Permissions.OrganizationHooks; v != nil {
permissions["organization_hooks"] = *v
}
if v := appInstallation.Permissions.OrganizationPlan; v != nil {
permissions["organization_plan"] = *v
}
if v := appInstallation.Permissions.OrganizationProjects; v != nil {
permissions["organization_projects"] = *v
}
if v := appInstallation.Permissions.OrganizationSecrets; v != nil {
permissions["organization_secrets"] = *v
}
if v := appInstallation.Permissions.OrganizationSelfHostedRunners; v != nil {
permissions["organization_self_hosted_runners"] = *v
}
if v := appInstallation.Permissions.OrganizationUserBlocking; v != nil {
permissions["organization_user_blocking"] = *v
}
if v := appInstallation.Permissions.Packages; v != nil {
permissions["packages"] = *v
}
if v := appInstallation.Permissions.Pages; v != nil {
permissions["pages"] = *v
}
if v := appInstallation.Permissions.PullRequests; v != nil {
permissions["pull_requests"] = *v
}
if v := appInstallation.Permissions.RepositoryHooks; v != nil {
permissions["repository_hooks"] = *v
}
if v := appInstallation.Permissions.RepositoryProjects; v != nil {
permissions["repository_projects"] = *v
}
if v := appInstallation.Permissions.RepositoryPreReceiveHooks; v != nil {
permissions["repository_pre_receive_hooks"] = *v
}
if v := appInstallation.Permissions.Secrets; v != nil {
permissions["secrets"] = *v
}
if v := appInstallation.Permissions.SecretScanningAlerts; v != nil {
permissions["secret_scanning_alerts"] = *v
}
if v := appInstallation.Permissions.SecurityEvents; v != nil {
permissions["security_events"] = *v
}
if v := appInstallation.Permissions.SingleFile; v != nil {
permissions["single_file"] = *v
}
if v := appInstallation.Permissions.Statuses; v != nil {
permissions["statuses"] = *v
}
if v := appInstallation.Permissions.TeamDiscussions; v != nil {
permissions["team_discussions"] = *v
}
if v := appInstallation.Permissions.VulnerabilityAlerts; v != nil {
permissions["vulnerability_alerts"] = *v
}
if v := appInstallation.Permissions.Workflows; v != nil {
permissions["workflows"] = *v
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use the Get*() accessors for these attributes to not have to deal with pointers?

And since there are so many fields, I wonder if using reflect to build a field iterator could make sense 🤔

Comment on lines 13 to 17
check := resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet("data.github_organization_app_installations.test", "installations.0.id"),
resource.TestCheckResourceAttrSet("data.github_organization_app_installations.test", "installations.0.slug"),
resource.TestCheckResourceAttrSet("data.github_organization_app_installations.test", "installations.0.app_id"),
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please inline this variable

Comment on lines 19 to 41
testCase := func(t *testing.T, mode string) {
resource.Test(t, resource.TestCase{
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: check,
},
},
})
}

t.Run("with an anonymous account", func(t *testing.T) {
t.Skip("anonymous account not supported for this operation")
})

t.Run("with an individual account", func(t *testing.T) {
t.Skip("individual account not supported for this operation")
})

t.Run("with an organization account", func(t *testing.T) {
testCase(t, organization)
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use this testCase pattern anymore

Suggested change
testCase := func(t *testing.T, mode string) {
resource.Test(t, resource.TestCase{
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: check,
},
},
})
}
t.Run("with an anonymous account", func(t *testing.T) {
t.Skip("anonymous account not supported for this operation")
})
t.Run("with an individual account", func(t *testing.T) {
t.Skip("individual account not supported for this operation")
})
t.Run("with an organization account", func(t *testing.T) {
testCase(t, organization)
})
resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessHasOrgs(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: config,
Check: check,
},
},
})

@atilsensalduz atilsensalduz requested a review from deiga January 16, 2026 13:37
@stevehipwell stevehipwell added this to the v6.11.0 Release milestone Jan 16, 2026
@stevehipwell stevehipwell added Type: Feature New feature or request New data source and removed Status: Stale Used by stalebot to clean house labels Jan 16, 2026
diofeher
diofeher previously approved these changes Jan 16, 2026
@stevehipwell
Copy link
Collaborator

@atilsensalduz could you please rebase this PR?

Copy link
Collaborator

@stevehipwell stevehipwell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @atilsensalduz, I've added a couple of comments. Could you also add support for single_file_paths, created_at & updated_at.

Computed: true,
Description: "The ID of the GitHub App installation.",
},
"slug": {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be app_slug?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you're right @stevehipwell app_slug is more explicit and consistent.

atilsensalduz and others added 5 commits January 30, 2026 22:56
…pp_installations data source

Signed-off-by: atilsensalduz <atil.sensalduz@gmail.com>
Co-authored-by: Steve Hipwell <steve.hipwell@gmail.com>
Co-authored-by: Steve Hipwell <steve.hipwell@gmail.com>
Co-authored-by: Steve Hipwell <steve.hipwell@gmail.com>
…pp_installations data source

Signed-off-by: atilsensalduz <atil.sensalduz@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

New data source Type: Feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants