TerraformでGithubのRepositoryをマネージしてみる

# なぜ

GithubのRepositoryの管理権限をマニュアルに管理していると各リポジトリの管理者権限をだれが持ってるのかが見えない (どっかの権限をいじれば、管理者がだれなのかを見せることができるのかもしれないがそんな機能があったら教えていただきたいです)

# コード化

[Github のTerraform provider]( https://registry.terraform.io/providers/integrations/github/latest)があるのでGithubのRepositoryとRepositoryの権限をコード化すれば情報が透明化できるし、確認もしやすくなるのでは？

# GithubをTerraformでマネージする準備 (Terraform Cloud)

1. `backend.tf` (今回はTerraform Cloud) と `provider.tf`を作成

    ```hcl:backend.tf
    terraform {
      backend "remote" {
        hostname     = "app.terraform.io"
        organization = "naka" # 自分のTerraform Cloudのアカウント名

        workspaces {
          name = "github" # 自分のアカウントの中のWorkspace名 任意で名前をつけられる
        }
      }
    }
    ```

    ```hcl:provider.tf
    terraform {
      required_providers {
        github = {
          source  = "integrations/github"
          version = "~> 4.0"
        }
      }
    }

    provider "github" {
      owner = "nakamasato" # 自分のGithubのアカウント名 例: https://github.com/nakamasato
    }
    ```

1. バックエンドへのアクセスをできるようにしておく
    1. バックエンドがTerraform Cloudの場合は`terraform login`しておく
    1. バックエンドがローカルの場合は不要
    1. バックエンドがS3などの場合はRead/Writeアクセス権を付与
1. `terraform init`を実行
    1. Terraform Cloudの場合は、Workspaceが作成される
1. `Github Token`を生成して環境変数に設定する (一旦 `repo` と `delete_repo` の権限を付与。管理するリソースの必要に応じて変更。)
    1. Terraform Cloudの場合、UIから環境変数を設定![Screen Shot 2021-07-08 at 10.04.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7059/3addc1a3-e9dc-e425-9ab4-1309ff6e90c8.png)
    1. それ以外の場合は、Terraformの変数で渡すか、 ローカルの`GITHUB_TOKEN` 環境変数をセット (https://registry.terraform.io/providers/integrations/github/latest/docs)

        ```hcl
        provider "github" {
          token = var.token # or `GITHUB_TOKEN`
        }
        ```

これで基本は自分の書きたいリソースをtf ファイルに書いてApplyすれば良くなる。準備完了

# Terraformでリソース作成する

## Repository

```hcl:repository.tf
resource "github_repository" "terraform-github-template" {
  name        = "terraform-github-template"
  description = "template repository create by terraform"

  visibility = "public"
}
```

## Repositoryのbranch protection

```hcl:branch_protection.tf
resource "github_branch_protection" "terraform-github-template-main" {
  repository_id = github_repository.terraform-github-template.node_id

  pattern          = "main"
  enforce_admins   = true
  allows_deletions = false
}
```

# Applyの自動化 (上記の準備でTerraform Cloudにした場合)

1. Terraform Cloudでコードがマージされたら自動でApplyしたい場合は、 Version Controlを設定して、Auto applyにチェックを入れる。
1. Version Controlで設定したRepoに、初PRを作成してTerraform Cloudが動いているか確認する
1. マージして自動でApplyされる

# 既存リソースのImport

例では、https://github.com/nakamasato/eks をインポート

1. tfファイルを用意
    1. 例

    ```hcl:import.tf
    resource "github_repository" "eks" {
      name                 = "eks"

      visibility = "public"
    }
    ```
1. インポート `terraform import github_repository.terraform terraform` (Terraform Cloudでは `terraform login`してる必要あり)
    1. 例
    ```
    terraform import github_repository.eks eks
    ```
1. (Optional) tfファイルの修正
    1. `terraform plan` すると自分で作成したtfとimportしたstateにDiffがある場合があるので、Planして　`No changes. Your infrastructure matches the configuration.` になるまで直す
    1. 例 最終的にはこんな感じ

    ```hcl:import.tf
    resource "github_repository" "eks" {
      name                 = "eks"
      description          = "Manage EKS cluster with Terraform (development purpose only)"
      allow_merge_commit   = false
      allow_rebase_merge   = false
      has_downloads        = true
      has_issues           = true
      has_projects         = true
      has_wiki             = true
      vulnerability_alerts = true
      topics = [
      ]

      visibility = "public"
    }
    ```
1. コードプッシュ (Terraform Cloudも変更なし)


**注意**: 

Terraform CloudにGITHUB_TOKENを環境変数で設定していても、importするときは、ローカルでも `GITHUB_TOKEN` を設定しておかないと以下のエラーが出る

```bash
terraform import github_repository.python-sample python-sample            
Acquiring state lock. This may take a few moments...
╷
│ Error: Cannot import non-existent remote object
│ 
│ While attempting to import an existing object to "github_repository.python-sample", the provider detected that no object exists with the given id. Only pre-existing
│ objects can be imported; check that the id is correct and that it is associated with the provider's configured region or endpoint, or use "terraform apply" to
│ create a new remote object for this resource.
```

GITHUB_TOKENを設定:

```
export GITHUB_TOKEN=xxxxx
```

再度挑戦：

```bash
terraform import github_repository.python-sample python-sample
Acquiring state lock. This may take a few moments...

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
```

https://github.com/integrations/terraform-provider-github/issues/647

# まとめ

Githubのレポやブランチ権限をTerraformで管理してみた。レポへの管理権限や誰がマージ権限を持つかなどは管理できそうで、誰が管理・マージ権限もってるかの情報がオープンにできそう。

# 参考

- [Github Repository](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository)
- [Github Branch Protection](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/branch_protection)
- [terraform-import failure: Cannot import non-existent remote objec](https://github.com/integrations/terraform-provider-github/issues/647)
- [Some resources can only be used in the context of an organization](https://github.com/integrations/terraform-provider-github/issues/501)
- [Error: This resource can only be used in the context of an organization, "foo" is a user](https://github.com/integrations/terraform-provider-github/issues/769)


# ToDo

- [ ] `Error: This resource can only be used in the context of an organization, "xxxxx" is a user.` というエラーが出てうまく管理できない事がある ([Some resources can only be used in the context of an organization](https://github.com/integrations/terraform-provider-github/issues/501) [Error: This resource can only be used in the context of an organization, "foo" is a user](https://github.com/integrations/terraform-provider-github/issues/769)にissueがあるがまだうまく解決できてないので次回)