integrations / terraform-provider-github

Terraform GitHub provider
https://www.terraform.io/docs/providers/github/
MIT License
870 stars 710 forks source link

GitHub Apps fail to bypass pull requests #1248

Open jcogilvie opened 1 year ago

jcogilvie commented 1 year ago

Terraform Version

1.0.11

Affected Resource(s)

Terraform Configuration Files

Note: I have tried it with both string slugs and integer Installation IDs to the same result.

resource "github_branch_protection" "master_with_reviews" {
  count = var.needs_reviews ? 1 : 0
  # workaround https://github.com/integrations/terraform-provider-github/issues/908
  repository_id  = can(base64decode(github_repository.repo.node_id)) ? github_repository.repo.node_id : github_repository.repo.name
  pattern        = github_repository.repo.default_branch
  enforce_admins = true
  required_pull_request_reviews {
    restrict_dismissals             = true
    require_code_owner_reviews      = true
    required_approving_review_count = var.number_of_approvers
    dismissal_restrictions          = var.github_teams_with_write_access[*].node_id
    # one for each installation:  mk1 and mk2 of the github soylent parallelizer app
    pull_request_bypassers = ["github-app-mki", "github-app-mkii"]
  }
  required_status_checks {
    strict   = true
    contexts = local.status_checks
  }
}

Debug Output

https://gist.github.com/jcogilvie/1c871779fc13f53a1d8117e6ab38a1a5

Expected Behavior

The branch protection is created, correctly referencing the App(s) in question.

Actual Behavior

The build errors saying it can't find an entity with a corresponding global ID (see output).

Steps to Reproduce

  1. terraform apply

Important Factoids

If I add the branch protection referencing the app in the UI, and change my TF to instead try to zero out the branch protection, here's the terraform "detected changes" diff:

  # module.test_infra_repo.github_branch_protection.master_with_reviews[0] has changed
  ~ resource "github_branch_protection" "master_with_reviews" {
        id                              = "BPR_kwDOHn30k84BmiML"
        # (9 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ pull_request_bypassers          = [
              - "github-app-mki",
              - "github-app-mkii",
            ]
            # (5 unchanged attributes hidden)
        }

        # (1 unchanged block hidden)
    }

This seems to me to indicate that if we just allow string slugs through validation in the provider, everything would work as expected. This seems to match the documentation for the API that says the "Properties of the bypass_pull_request_allowances object" takes a list of slugs.

References

Are there any other GitHub issues (open or closed) or Pull Requests that should be linked here? For example:

Plouc314 commented 1 year ago

Hello @jcogilvie, I had the exact same problem and managed to get my app to bypass the branch protection.

First, the documentation of github on terraform states that you should pass "actor IDs" (as opposed to github REST doc that states to put slugs...).

Second, actor ID is in fact node ID as you could more or less guess from the terraform doc example. You can get the node ID of your app from this REST endpoint, but for that you need a token that you can get following this doc: Authenticating as a GitHub App.

Also, there is a pull requests (https://github.com/integrations/terraform-provider-github/pull/1268) that add github app as data source, that would make it easier to obtain the node ID.

Hope this helps

jcogilvie commented 1 year ago

@Plouc314 I was able to bypass the initial error with your help, but now I'm getting "Resource not accessible by integration" when trying to update a branch protection rule, even though my app has admin:read and admin:write, so terraform is failing to actually apply this update. Do you have any ideas?

patrickmarabeas commented 1 year ago

@jcogilvie if you are using a GitHub App to auth in your Terraform provider - then the app you are referencing in your resource will need to be public not private

devopsrick commented 1 year ago

@jcogilvie if you are using a GitHub App to auth in your Terraform provider - then the app you are referencing in your resource will need to be public not private

That seems like an odd requirement. Is there a documented example workflow with requirements for setting up github app auth for use with this provider? The provider docs themselves don't seem to contain that information.

Plouc314 commented 1 year ago

@jcogilvie if you are using a GitHub App to auth in your Terraform provider - then the app you are referencing in your resource will need to be public not private

My app is private to an organization.

@jcogilvie My app has read & write access to about all Repository permissions, including: Administration, Pull requests and Contents. Perhaps you are missing some permissions ?

jcogilvie commented 1 year ago

I definitely have admin, PR, and contents permissions.

One potential complication is that I am auth'd to the terraform provider as a private app, and I am trying to add that same app and also another private app in our same org to the list of bypassers.

App 1 and app 2 both have the same permissions.

Edit: It does succeed if I try to add just the currently-authed app. So the issue is somewhere in having the auth'd app add a different (also private) app.

patrickmarabeas commented 1 year ago

@jcogilvie if you are using a GitHub App to auth in your Terraform provider - then the app you are referencing in your resource will need to be public not private

That seems like an odd requirement. Is there a documented example workflow with requirements for setting up github app auth for use with this provider? The provider docs themselves don't seem to contain that information.

The referenced App within e.g. pull_request_bypassers needs to be public, not the App being used for the Terraform Provider auth. That can be whatever you want.

If you try to hit the /apps/APP_SLUG endpoint authenticated as an App (installation access token), the documentation suggests that this is possible:

If the GitHub App you specify is public, you can access this endpoint without authenticating. If the GitHub App you specify is private, you must authenticate with a personal access token or an installation access token to access this endpoint.

However you'll hit the following error:

403 Resource not accessible by integration []

If the App (APP_SLUG) is made public, you'll be able to hit the endpoint.

My guess is it's just poorly worded to say that the App can self identify against the endpoint - but not look up other private Apps.

¯\(ツ)

RobCannon commented 1 year ago

I think making the App public is a bug somewhere. You can add a private App to the list via the UI and it works correctly.

audunsolemdal commented 1 year ago

Second, actor ID is in fact node ID as you could more or less guess from the terraform doc example. You can get the node ID of your app from this REST endpoint, but for that you need a token that you can get following this doc: Authenticating as a GitHub App.

Edit: managed to get it working by the util installed from this guide: https://trstringer.com/github-api-requests-with-jwt/

hey @Plouc314

My org uses this action to fetch tokens in order to run rest API calls towards gh api. E.g. the contents api targetting another repository. This, which works just fine. However, this API seems to not accept the same token for some reason. Just wondering if you've encountered this error earlier?

response:

{
  "message": "A JSON web token could not be decoded",
  "documentation_url": "https://docs.github.com/rest"
}

sample workflow to use token:

    steps:
      - name: Generate token for target repo
        id: generate_token
        uses: tibdex/github-app-token@v1
        with:
          app_id: "218414"
          private_key: ${{ secrets.ghapp_pem }}

      - name: fetch app info
        shell: bash
        env:
          TOKEN: ${{ steps.generate_token.outputs.token }}
        run: |
          curl -i \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer ${TOKEN}" \
            https://api.github.com/app
Nexus2k commented 1 year ago

We're currently facing the same issue. App 1 is used to run terraform provider to read/modify branch_protection rules of repo X, where private App 2 is a protection bypasser. This is clearly a bug in the provider. What's the ETA on fixing this?

audunsolemdal commented 1 year ago

I am able to get Github apps to bypass pull requests while I am using PAT authentication for the Terraform provider. However, the TFPlan always states that the branch_protection resource will be changed.

        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ pull_request_bypassers          = [
              + "A_xxxxzzzz",
              + "A_xxxxyyyy,
            ]
            # (5 unchanged attributes hidden)
        }

The changes above are already applied. Did anyone find a solution for this apart from adding an ignore_changes block?

chris3ware commented 1 year ago

Unfortunately one GitHub App cannot query the API endpoint: https://github.com/settings/apps/:app_slug of another private App. Therefore the issue is with GitHub and not the Terraform Provider.

See response from GitHub support:

We've checked with our engineering team and currently private GitHub Apps can only use this endpoint on themselves. This is working as designed. We are currently looking into enabling some sort of read access to private apps. We can't say when or if it will be released but we'd recommend that you keep an eye on the Changelog where updates are announced.

jwhy89 commented 11 months ago

I am able to get Github apps to bypass pull requests while I am using PAT authentication for the Terraform provider. However, the TFPlan always states that the branch_protection resource will be changed.

        # (10 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ pull_request_bypassers          = [
              + "A_xxxxzzzz",
              + "A_xxxxyyyy,
            ]
            # (5 unchanged attributes hidden)
        }

The changes above are already applied. Did anyone find a solution for this apart from adding an ignore_changes block?

The ignore_changes block will not work either because it's still trying to read the value of the other private github apps. It temporarily worked with a terraform plan when I added the terraform github app alongside the other private github app but then the terraform apply didnt work.

NunoCardia commented 7 months ago

This is what worked for me:

  data "github_app" "terraform-app" {
      slug = <app_name>
  }

  resource "github_branch_protection" "protected_default_branch" {
    ...
    required_pull_request_reviews {
      ...
      pull_request_bypassers          = [data.github_app.terraform-app.node_id]
    }
  }

Also for repository rulesets you can use the github_app to pass the id

resource "github_repository_ruleset" "common" {
  .....
  bypass_actors {
    actor_id    = data.github_app.terraform-app.id
    actor_type  = ....
    bypass_mode = "always"
  }
  ....
}

It is not on the documentation but the github_app data source exports your App's ID.

isometry commented 2 weeks ago

… It is not on the documentation but the github_app data source exports your App's ID.

Unfortunately, this necessarily only works for the GitHub App with which you're running Terraform. If you attempt to use the same trick to assign any privileges to a separate GitHub App, whilst running Terraform with GitHub App credentials, it will fail.