integrations / terraform-provider-github

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

Some resource settings always change on apply #1019

Closed wobo-mattmencel closed 6 months ago

wobo-mattmencel commented 2 years ago

This is happening on existing resources that are imported in to Terraform. I haven't confirmed, but am guessing if they were new I would not see this behavior.

Terraform continuously updates several settings on resources on every apply. Viewing the settings in the UI reveals they are set correctly according to the configuration in Terraform.

If I terraform state pull, edit the state to reflect these values that want to keep changing, and then push the state back, then it seems to stick. It no longer needs to update those attributes. I just updated one attribute to test that and it's stayed correct so far.

Terraform Version

Terraform v1.1.2
on darwin_amd64
+ provider registry.terraform.io/cloudposse/utils v0.17.10
+ provider registry.terraform.io/hashicorp/azurerm v2.90.0
+ provider registry.terraform.io/integrations/github v4.19.0

Affected Resource(s)

Terraform Configuration Files

resource "github_repository" "this" {
  name        = var.name
  description = var.description
  visibility  = "private"

  allow_auto_merge       = true
  allow_merge_commit     = true
  allow_rebase_merge     = false
  allow_squash_merge     = false
  archive_on_destroy     = true
  auto_init              = true
  delete_branch_on_merge = true
  has_downloads          = false
  has_issues             = true
  has_projects           = false
  has_wiki               = true
  vulnerability_alerts   = true

  lifecycle {
    prevent_destroy = true
  }
}

resource "github_branch_protection" "this" {
  for_each = { for branch in var.protected_branches : branch.branch_name => branch }

  repository_id                   = github_repository.this.node_id
  pattern                         = each.value.branch_name
  enforce_admins                  = true
  allows_deletions                = false
  allows_force_pushes             = false
  require_conversation_resolution = true

  required_pull_request_reviews {
    dismiss_stale_reviews           = true
    required_approving_review_count = 1
  }

  required_status_checks {
    strict   = true
    contexts = each.value.status_check_contexts
  }

}

Expected Behavior

There should be no changes detected.

Actual Behavior

Terraform apply always has these changes to apply.

Terraform will perform the following actions:

  # module.repos["foo"].github_repository.this will be updated in-place
  ~ resource "github_repository" "this" {
      ~ allow_auto_merge       = false -> true
      ~ allow_merge_commit     = false -> true
      ~ delete_branch_on_merge = false -> true
        id                     = "foo"
        name                   = "foo"
        # (26 unchanged attributes hidden)
    }

# module.repos["foo"].github_branch_protection.this["main"] will be updated in-place
  ~ resource "github_branch_protection" "this" {
      ~ enforce_admins                  = false -> true
        id                              = "bar="
        # (8 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

Plan: 0 to add, 2 to change, 0 to destroy.

Steps to Reproduce

  1. terraform apply
jgreat commented 2 years ago

I'm also hitting this issue with an upgrade to 4.19.0

4.18.2 does not have this issue.

jcudit commented 2 years ago

Hmm, possibly related to the branch protection updates we shipped over in v4.19.0, but it is not immediately obvious to me how 🤔

samuel-phan commented 2 years ago

I've tested with 4.18.2 and I still get the problem:

  # github_repository.org_configuration will be updated in-place
  ~ resource "github_repository" "org_configuration" {
      ~ allow_auto_merge       = false -> true
        id                     = "org-configuration"
        name                   = "org-configuration"
        # (26 unchanged attributes hidden)
    }

The allow_auto_merge is always flagged to false -> true.

If I check manually the option on the web UI, terraform apply will uncheck it.

$ terraform version
Terraform v1.1.4
on darwin_amd64
+ provider registry.terraform.io/integrations/github v4.18.2

GitHub Enterprise Server 3.1.14
alexbde commented 2 years ago

I can confirm with GHES 3.2.10, Terraform 1.1.7, and provider v4.20.1.

Running TF_LOG=DEBUG terraform apply you can see:

  # module.repository_devops-example.github_repository.repository will be updated in-place
  ~ resource "github_repository" "repository" {
      ~ allow_auto_merge       = false -> true
        id                     = "devops-example"
        name                   = "devops-example"
        # (26 unchanged attributes hidden)
    }

=> yes

[DEBUG] provider.terraform-provider-github_v4.20.1.exe: 2022/03/22 11:03:25 [DEBUG] Github API Request Details:
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: ---[ REQUEST ]---------------------------------------
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: PATCH /api/v3/repos/<redacted>/devops-example HTTP/1.1
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Host: <redacted>
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: User-Agent: go-github
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Content-Length: 370
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Accept: application/vnd.github.baptiste-preview+json, application/vnd.github.nebula-preview+json
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Content-Type: application/json
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Accept-Encoding: gzip
[DEBUG] provider.terraform-provider-github_v4.20.1.exe:
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: {
[DEBUG] provider.terraform-provider-github_v4.20.1.exe:  "name": "devops-example",
...
[DEBUG] provider.terraform-provider-github_v4.20.1.exe:  "allow_auto_merge": true,
...
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: }

and later

[DEBUG] provider.terraform-provider-github_v4.20.1.exe: 2022/03/22 11:03:28 [DEBUG] Github API Response Details:
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: ---[ RESPONSE ]--------------------------------------
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: HTTP/2.0 200 OK
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Access-Control-Allow-Origin: *
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining
, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, Deprecation, Sunset
[WARN]  Provider "provider[\"registry.terraform.io/integrations/github\"]" produced an unexpected new value for module.repository_devops-sonarqube-test.github_repository.repository, but we are tolerating it because it is using the legacy plugin SDK.
    The following problems may be the cause of any confusing errors from downstream operations:
      - .etag: was cty.StringVal("W/\"f8...03\""), but now cty.StringVal("W/\"81...0a\"")  
      - .allow_auto_merge: was cty.True, but now cty.False
[DEBUG] provider.terraform-provider-github_v4.20.1.exe: Cache-Control: private, max-age=60, s-maxage=60
[WARN]  Provider "provider[\"registry.terraform.io/integrations/github\"]" produced an unexpected new value for module.repository_devops-example.github_repository.repository, but we are tolerating it because it is using the legacy plugin SDK.
    The following problems may be the cause of any confusing errors from downstream operations:
      - .etag: was cty.StringVal("W/\"34...5d\""), but now cty.StringVal("W/\"cb...a5\"")  
      - .allow_auto_merge: was cty.True, but now cty.False

Furthermore, there's this in the state:

    {
      "module": "module.repository_devops-example",
      "mode": "managed",
      "type": "github_repository",
      "name": "repository",
      "provider": "provider[\"registry.terraform.io/integrations/github\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "allow_auto_merge": false,
            "allow_merge_commit": false,
            "allow_rebase_merge": false,
...

Regarding the first posts'

This is happening on existing resources that are imported in to Terraform. I haven't confirmed, but am guessing if they were new I would not see this behavior.

adding a new repository:

  # module.repository_devops-new-example.github_repository.repository will be created
  + resource "github_repository" "repository" {
      + allow_auto_merge       = true
      + allow_merge_commit     = false
      + allow_rebase_merge     = false
      + allow_squash_merge     = true
      ...
      + id                     = (known after apply)
      + name                   = "devops-new-example"
      ...
    }

=> yes

[WARN]  Provider "provider[\"registry.terraform.io/integrations/github\"]" produced an unexpected new value for module.repository_devops-new-example.github_repository.repository, but we are tolerating it because it is using the legacy plugin SDK.
    The following problems may be the cause of any confusing errors from downstream operations:
      - .homepage_url: was null, but now cty.StringVal("")
      - .description: was null, but now cty.StringVal("")
      - .allow_auto_merge: was cty.True, but now cty.False
      - .vulnerability_alerts: was null, but now cty.False
      - .has_downloads: was null, but now cty.False
      - .is_template: was null, but now cty.False

So, it is also not working for new repositories (I can confirm via web ui that the checkbox is unchecked).

Downgrading to provider 4.18.2 as suggested:

[DEBUG] provider.terraform-provider-github_v4.18.2.exe: X-Github-Enterprise-Version: 3.2.10
[WARN]  Provider "provider[\"registry.terraform.io/integrations/github\"]" produced an unexpected new value for module.repository_devops-example.github_repository.repository, but we are tolerating it because it is using the legacy plugin SDK.
    The following problems may be the cause of any confusing errors from downstream operations:
      - .allow_auto_merge: was cty.True, but now cty.False
      - .etag: was cty.StringVal("W/\"c5...9f\""), but now cty.StringVal("W/\"96...a3\"")

Same for the newly created repository.

Downgrading to 4.17.0 (in which allow_auto_merge was added):

[DEBUG] provider.terraform-provider-github_v4.17.0.exe: X-Github-Enterprise-Version: 3.2.10
[WARN]  Provider "provider[\"registry.terraform.io/integrations/github\"]" produced an unexpected new value for module.repository_devops-example.github_repository.repository, but we are tolerating it because it is using the legacy plugin SDK.
    The following problems may be the cause of any confusing errors from downstream operations:
      - .etag: was cty.StringVal("W/\"96...a3\""), but now cty.StringVal("W/\"1f...f5\"")  
      - .allow_auto_merge: was cty.True, but now cty.False

Same same :(

Downgrading to Terraform 1.0.11 (out of curiousity) returns the same result.

@jcudit any idea what else we can try to solve this issue?

gwkunze commented 2 years ago

I think I'm experiencing the same problem:

  ~ resource "github_branch_protection" "protect-main" {
        id                              = "redacted"
        # (9 unchanged attributes hidden)

      ~ required_pull_request_reviews {
          ~ dismiss_stale_reviews           = false -> true
          ~ require_code_owner_reviews      = true -> false
            # (3 unchanged attributes hidden)
        }

      - required_status_checks {
          - contexts = [] -> null
          - strict   = true -> null
        }
    }

This appears for all our branch protection rules on every apply, and the changes are never actually made. If I make the changes manually, they do disappear here though.

alexbde commented 2 years ago

Interesting fact: setting allow_auto_merge via simple API call is also not working:

curl -X PATCH -u '<redacted>' -H 'Accept: application/vnd.github.v3+json' https://<redacted>/api/v3/repos/<redacted>/devops-example -d '{"allow_auto_merge": true}'

In fact, the answer is not even containing the allow_auto_merge attribute:

``` { "id": , "node_id": "", "name": "devops-example", "full_name": "/devops-example", "private": true, "owner": { "login": "", "id": , "node_id": "", "avatar_url": "/avatars/u/?", "gravatar_id": "", "url": "/api/v3/users/", "html_url": "/", "followers_url": "/api/v3/users//followers", "following_url": "/api/v3/users//following{/other_user}", "gists_url": "/api/v3/users//gists{/gist_id}", "starred_url": "/api/v3/users//starred{/owner}{/repo}", "subscriptions_url": "/api/v3/users//subscriptions", "organizations_url": "/api/v3/users//orgs", "repos_url": "/api/v3/users//repos", "events_url": "/api/v3/users//events{/privacy}", "received_events_url": "/api/v3/users//received_events", "type": "Organization", "site_admin": false }, "html_url": "//devops-example", "description": null, "fork": false, "url": "/api/v3/repos//devops-example", "forks_url": "/api/v3/repos//devops-example/forks", "keys_url": "/api/v3/repos//devops-example/keys{/key_id}", "collaborators_url": "/api/v3/repos//devops-example/collaborators{/collaborator}", "teams_url": "/api/v3/repos//devops-example/teams", "hooks_url": "/api/v3/repos//devops-example/hooks", "issue_events_url": "/api/v3/repos//devops-example/issues/events{/number}", "events_url": "/api/v3/repos//devops-example/events", "assignees_url": "/api/v3/repos//devops-example/assignees{/user}", "branches_url": "/api/v3/repos//devops-example/branches{/branch}", "tags_url": "/api/v3/repos//devops-example/tags", "blobs_url": "/api/v3/repos//devops-example/git/blobs{/sha}", "git_tags_url": "/api/v3/repos//devops-example/git/tags{/sha}", "git_refs_url": "/api/v3/repos//devops-example/git/refs{/sha}", "trees_url": "/api/v3/repos//devops-example/git/trees{/sha}", "statuses_url": "/api/v3/repos//devops-example/statuses/{sha}", "languages_url": "/api/v3/repos//devops-example/languages", "stargazers_url": "/api/v3/repos//devops-example/stargazers", "contributors_url": "/api/v3/repos//devops-example/contributors", "subscribers_url": "/api/v3/repos//devops-example/subscribers", "subscription_url": "/api/v3/repos//devops-example/subscription", "commits_url": "/api/v3/repos//devops-example/commits{/sha}", "git_commits_url": "/api/v3/repos//devops-example/git/commits{/sha}", "comments_url": "/api/v3/repos//devops-example/comments{/number}", "issue_comment_url": "/api/v3/repos//devops-example/issues/comments{/number}", "contents_url": "/api/v3/repos//devops-example/contents/{+path}", "compare_url": "/api/v3/repos//devops-example/compare/{base}...{head}", "merges_url": "/api/v3/repos//devops-example/merges", "archive_url": "/api/v3/repos//devops-example/{archive_format}{/ref}", "downloads_url": "/api/v3/repos//devops-example/downloads", "issues_url": "/api/v3/repos//devops-example/issues{/number}", "pulls_url": "/api/v3/repos//devops-example/pulls{/number}", "milestones_url": "/api/v3/repos//devops-example/milestones{/number}", "notifications_url": "/api/v3/repos//devops-example/notifications{?since,all,participating}", "labels_url": "/api/v3/repos//devops-example/labels{/name}", "releases_url": "/api/v3/repos//devops-example/releases{/id}", "deployments_url": "/api/v3/repos//devops-example/deployments", "created_at": "2021-04-14T09:35:52Z", "updated_at": "2022-03-22T10:43:20Z", "pushed_at": "2022-03-22T13:00:09Z", "git_url": "git:////devops-example.git", "ssh_url": "git@:/devops-example.git", "clone_url": "//devops-example.git", "svn_url": "//devops-example", "homepage": "", "size": 38, "stargazers_count": 0, "watchers_count": 0, "language": "Java", "has_issues": false, "has_projects": false, "has_downloads": false, "has_wiki": false, "has_pages": false, "forks_count": 0, "mirror_url": null, "archived": false, "disabled": false, "open_issues_count": 2, "license": null, "forks": 0, "open_issues": 2, "watchers": 0, "default_branch": "main", "permissions": { "admin": true, "maintain": true, "push": true, "triage": true, "pull": true }, "allow_squash_merge": true, "allow_merge_commit": false, "allow_rebase_merge": false, "delete_branch_on_merge": true, "organization": { "login": "", "id": , "node_id": "", "avatar_url": "/avatars/u/?", "gravatar_id": "", "url": "/api/v3/users/", "html_url": "/", "followers_url": "/api/v3/users//followers", "following_url": "/api/v3/users//following{/other_user}", "gists_url": "/api/v3/users//gists{/gist_id}", "starred_url": "/api/v3/users//starred{/owner}{/repo}", "subscriptions_url": "/api/v3/users//subscriptions", "organizations_url": "/api/v3/users//orgs", "repos_url": "/api/v3/users//repos", "events_url": "/api/v3/users//events{/privacy}", "received_events_url": "/api/v3/users//received_events", "type": "Organization", "site_admin": false }, "network_count": 0, "subscribers_count": 0 } ```

...which explains, why Terraform is recognizing the option as set to false.

I'll contact the GitHub support to clarify why the API is not working in GHES 3.2.10.

israelbgf commented 2 years ago

I have also the same problem, it doesn't show the changes in the plan, but definitely always reset the two attributes:

  allow_auto_merge
  delete_branch_on_merge

Even if I use the ignore_changes lifecycle (Provider version: 4.28.0)

drsherluck commented 2 years ago

I am having the same problem (Provider version 4.29.0) I think it might be an authentication/permissions issue which causes data to be missing from api calls. It works with if I use PAT locally, but not when using a GitHub App

alexbde commented 2 years ago

GitHub confirmed the GHES version is the root cause. It is not working with GHES 3.2, it starts working with GHES >= 3.3.

jazzlyn commented 2 years ago

Hi,

same problem here, Provider 4.28.0. I can confirm that it occurs with the Github App, it works flawlessly with the personal access token.

We are on the Github free cloud plan, so it's not only related to GHES.

Horgix commented 1 year ago

Joining in to confirm the symptoms:

As pointed by @jazzlyn and now that it's mentioned, the issue was not showing up when using personnal access tokens; it started when we switched to a GitHub App.


bodgit commented 1 year ago

I think I'm hitting the same issue, every Terraform plan shows this for every repository:

  # github_repository.repository will be updated in-place
  ~ resource "github_repository" "repository" {
      ~ allow_merge_commit          = false -> true
      ~ delete_branch_on_merge      = false -> true
        id                          = "repository_name"
      + merge_commit_message        = "PR_TITLE"
      + merge_commit_title          = "MERGE_MESSAGE"
        name                        = "repository_name"
      + squash_merge_commit_message = "COMMIT_MESSAGES"
      + squash_merge_commit_title   = "COMMIT_OR_PR_TITLE"
        # (27 unchanged attributes hidden)
    }

I have ~ 30 repositories all configured the same and every one shows the same diff on plan, yet (at least for allow_merge_commit & delete_branch_on_merge) those settings are correctly enabled in the repository settings.


bodgit commented 1 year ago

I'm currently configuring the provider like so:

provider "github" {
  owner = "MyOrg"

  app_auth {
    installation_id = "12345678"
  }
}

I'm then exporting the GITHUB_APP_ID and GITHUB_APP_PEM_FILE environment variables to finish configuring the provider. If I change the provider like so:

provider "github" {
  owner = "MyOrg"

  # app_auth {
  #   installation_id = "12345678"
  # }
}

and then export GITHUB_TOKEN with a classic PAT scoped to admin:org and repo then my plans go back to being a no-op. So it definitely seems related to PAT vs App auth. Is this a GitHub bug perhaps?

My App has the following permissions:

I can't use a new Fine-grained PAT scoped to the same permissions as that doesn't work with the GraphQL API.

bodgit commented 1 year ago

I've done some more digging. Using the following command:

curl -H "Accept: application/vnd.github+json" \
     -H "Authorization: Bearer <TOKEN>" \
     -H "X-GitHub-Api-Version: 2022-11-28" \
     https://api.github.com/repos/MyOrg/my-repo

I run the command once with a PAT, and again using an access token obtained through my App following this documentation. Here's the difference in output:

--- PAT 2023-03-14 15:28:16.947431000 +0000
+++ APP 2023-03-14 15:51:47.137431000 +0000
@@ -100,24 +100,12 @@
   "watchers": 0,
   "default_branch": "master",
   "permissions": {
-    "admin": true,
-    "maintain": true,
-    "push": true,
-    "triage": true,
-    "pull": true
+    "admin": false,
+    "maintain": false,
+    "push": false,
+    "triage": false,
+    "pull": false
   },
-  "temp_clone_token": "XXX",
-  "allow_squash_merge": false,
-  "allow_merge_commit": true,
-  "allow_rebase_merge": false,
-  "allow_auto_merge": false,
-  "delete_branch_on_merge": true,
-  "allow_update_branch": false,
-  "use_squash_pr_title_as_default": false,
-  "squash_merge_commit_message": "COMMIT_MESSAGES",
-  "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
-  "merge_commit_message": "PR_TITLE",
-  "merge_commit_title": "MERGE_MESSAGE",
   "organization": {
     "login": "MyOrg",
     "id": 12345678,

So you can see all the problematic fields just aren't returned by the API when using an App to authenticate but are present when using a PAT. Terraform cannot possibly work if the API has this behaviour.

bodgit commented 1 year ago

I contacted GitHub support about this. Adding the Repository Contents R/W permission to the App permissions fixes it; my Terraform runs are now no-ops once more.

This seems slightly counter-intuitive as my App has now gained the ability to push contents in order to retrieve what are essentially repository settings. I'm checking with GitHub support if this is indeed the intended behaviour.

coyotespike commented 1 year ago

Chiming in to say I'm also currently hitting this issue. It is not an absolute blocker, but it is of course annoying to mentally filter out part of the plan as "not really changes."

With a fine-grained token, I get

401 Unauthorized body: "{\"message\":\"Personal access tokens with fine grained access do not support the GraphQL API\",\"documentation_url\":\"https://docs.github.com/graphql/guides/forming-calls-with-graphql#authenticating-with-graphql\"}"

I don't have an App, just running from the repository. Thanks for the detective work @bodgit

bodgit commented 1 year ago

So I finally got a response from GitHub support:

On github.com, only users with push access can figure out which merge options are available because only users with push access can merge pull requests and see which options are provided by the UI. The REST API is behaving the same way the website UI does.

So it looks like this is why the App needs to have contents R/W permission, in order just to see which merge options are available.

kfcampbell commented 1 year ago

Hmm...we could start with a docs fix for now. I'm not sure what a good option would be for behavior in the provider.

estrella86 commented 1 year ago

Thank you so much for the investigation @bodgit - I was able to fix this error to the repo by the fine grained permission "contents" with r/w

github-actions[bot] commented 6 months ago

👋 Hey Friends, this issue 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!