CircleCI の機能を Terraform Provider で設定・管理する

こんにちは。CircleCI カスタマーサクセスチームの Chisato です。

最近、野菜を7種類ほど育て始めました。
毎朝の水やりで成長を感じるのが日々の楽しみです。

今回は、CircleCI のサポートエンジニアである Kelvin さんが作成した、CircleCI のための Terraform プロバイダー（Terraform Provider for CircleCI）をご紹介します。
この Terraform プロバイダーは、まだ CircleCI の公式ではないため、ご利用の際はご注意ください。


# 準備するもの
* CircleCIのアカウント と プロジェクト
* Terraform


# Terraform の設定
[Terraform](https://developer.hashicorp.com/terraform) は、HashiCorp社 が提供する IaC (Infrastructure as Code)のツールで、構築、変更、バージョン管理といったインフラの構成管理を安全かつ効率的に実行可能にします。

[こちらのページ](https://developer.hashicorp.com/terraform/downloads) にアクセスして、Terraform をインストールします。
macOS のオープンソース版は Homebrew でインストールできます。

```Terminal:Terminal
~ % brew tap hashicorp/tap
~ % brew install hashicorp/tap/terraform
```

正しくインストールされたか確認するには、`terraform version`のコマンドを用います。

```Terminal:Terminal
~ % terraform version
Terraform v1.4.5
on darwin_amd64

```



# Terraform プロバイダー

Terraform のプロバイダーを用いると、あらゆるサービスの API と連携できます。
たとえば、Terraform の[公式チュートリアル](https://developer.hashicorp.com/terraform/tutorials/aws-get-started)では、AWS EC2 を作成するプロバイダーが紹介されています。
Terraform の　[Registry](https://registry.terraform.io/browse/providers) では、さまざまなプロバイダーを検索・利用できます。

[今回ご紹介するプロバイダー](https://registry.terraform.io/providers/kelvintaywl/circleci/latest)を用いると、CircleCI の下記のリソースを簡単に作成・管理できます。

下記リソースは、通常CircleCIのUIから作成するものです。
たとえば環境変数は、一度作成すると値をどこかに保存しておく必要があったり、管理や削除のために該当のページにアクセスしたりする手間が発生します。

一方、このプロバイダーを使うと、下記リソースの作成/設定、編集、削除が簡単に Terraform の *.tf ファイルで実行できます。

* Project Webhook （ウェブフック）
* Project Scheduled Pipeline（パイプラインのスケジュール実行）
* Project Environment Variables（環境変数）
* Project Checkout key（チェックアウトキー）
* Context（コンテキスト）
* Context Environment Variables（コンテキストの環境変数）
<br>

# Webhook を作成・管理する

Terraform は名前が強そうでかっこいいので、プロバイダーの設定も複雑なのかと構えていましたが、実際は非常にシンプルでした。

ここでは、Webhookを作成してみます。

1\. 対象プロジェクトのディレクトリ内に、`main.tf` というファイルを作成します。

2\. [プロバイダーのページ](https://registry.terraform.io/providers/kelvintaywl/circleci/latest)にアクセスして、`USE PROVIDER` をクリックします。下図のピンク色で囲ったコード部分をクリックしてコピーします。または、図の下のコードをコピーして利用できます。

![Screenshot 2023-04-25 at 14.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/455b8552-bb2d-0d17-9a14-c6104bdbbd4c.png)

```Terraform:Terraform Provider
terraform {
  required_providers {
    circleci = {
      source = "kelvintaywl/circleci"
      version = "0.9.3"
    }
  }
}

provider "circleci" {
  # Configuration options
}

```
<br>
<br>

3\. `main.tf` の中に、コピーしたコードをペーストして保存します。

4\. プロバイダーのページで `Documentation` をクリックします。次に、遷移先ページ左側の `Resources` のドロップダウンメニューをクリックして展開します。 `Resources`欄には、機能の作成・管理方法が記載されています。

5\. 機能一覧から使いたい機能をクリックし、`Example Usage` 下部のコードをコピーします。Webhookは下記のコードをコピーして使用できます。
こちらも、`main.tf` を開き、先ほどペーストしたコードの下にペーストします。
![Screenshot 2023-04-25 at 14.54.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/751e0492-0379-b6ef-cc42-234afad801f0.png)

```Terraform:circleci_webhook Example Usage
data "circleci_project" "my_project" {
  slug = "github/acmeorg/foobar"
}

resource "circleci_webhook" "my_webhook" {
  project_id     = data.circleci_project.my_project.id
  name           = "my_webhook"
  url            = "https://example.com/hook"
  signing_secret = "5uperSeCr3t!"
  verify_tls     = true
  events = [
    // accepts only "workflow-completed" and "job-completed"
    "job-completed",
    "workflow-completed"
  ]
}

output "webhooks" {
  description = "my_webhook_id"
  value       = circleci_webhook.my_webhook.id
}

```
<br>
<br>


<br>

5\. CircleCI を開き、該当するプロジェクトの`Project Settings`ページに移動します。
![Screenshot 2023-04-24 at 23.01.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/95823727-7084-0dcc-1132-e23407c50678.png)
下記のピンク色で囲ったアイコンをクリックして、Project IDをコピーします。
![Screenshot 2023-04-24 at 23.00.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/c45b9ba0-0d44-7854-9ad3-378831712b03.png)

<br>
<br>

6\. `main.tf` ファイルを開いて、`project_id = "プロジェクトID"` 部分に先ほどコピーしたIDをペーストします。


```terraform:main.tf
terraform {
  required_providers {
    circleci = {
      source = "example.com/kelvintaywl/circleci"
    }
  }
}

provider "circleci" {
  // api_token = ""; or set via CIRCLE_TOKEN
  // hostname = "https://circleci.com"; or set via CIRCLE_HOSTNAME
}

locals {
  // 下記に CircleCI の project ID をペースト
  project_id = "プロジェクトID"
}

resource "circleci_webhook" "my_webhook" {
  project_id     = local.project_id
  name           = "my_webhook"
  url            = "WebhookのためのURL"
  signing_secret = "5uperSeCr3t!"
  verify_tls     = true
  events = [
    // accepts only "workflow-completed" and "job-completed"
    "job-completed",
    "workflow-completed"
  ]
}

output "webhooks" {
  description = "my_webhook_id"
  value       = circleci_webhook.my_webhook.id
}


```

<br>
<br>

7\.  `main.tf` の `provider "circleci"` 部分は、コメントアウトしてあります。
CircleCIとの連携には、環境変数( Enviroment Variables )、またはパーソナル API トークン（ Personal API Token ）を利用できます。
今回はパーソナル API トークンを `.bash_profile` に保存して、 CircleCI と連携しました。
パーソナルAPIトークンは、CircleCIのダッシュボード左下にある自分のアイコンをクリックし、[`User Settings` ページ](https://app.circleci.com/settings/user)の `Personal API Tokens` タブから作成できます。 

```bash:.bash_profile
export CIRCLE_TOKEN="パーソナルAPIトークン"

```
<br>

8\. 今回は Webhook との連携に [AirTable](https://airtable.com/) を使用しました。
Airtable 上で `Create a base` 、画面上部の `Automations` と進み、`When webhook received` を選択します。
その他の設定は、下図を参照ください。今回は、特定のワークフローが走るたびに、「ジョブごとのID」、「ジョブの名前」、「ビルド結果」を取得できるように設定しました。
最後にトグル（ピンクで囲った部分）を ON にするのをお忘れなく。
![Screenshot 2023-04-26 at 15.19.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/13bc613f-5338-97d5-e8dd-205cdeab3d43.png)
最後に、右側の設定画面に表示されたURLをコピーします。
![Screenshot 2023-04-26 at 15.40.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/ee717230-526f-fee0-039f-2f97b9068d7b.png)

9\. `main.tf` のファイルで、`resource` の `url = "WebhookのためのURL"` に先ほどコピーしたURLをペーストします。


<br>

10\. `terraform init` のコマンドを実行します。"Terraform has been successfully initialized!" というメッセージが返ってきたら、次に進みます。

```Terminal:Terminal
% terraform init

```
<br>
<br>

9\. どのような変更が加えられるかをレビューするため、下記コマンドを実行します。

```Termiinal:Terminal

~ % terraform plan

```
<br>
<br>

10\. 内容のレビューが完了したら、下記コマンドで実際にWebhookを作成します。実行確認のメッセージが出てくるので、`yes` と入力します。

```Termiinal:Terminal

~ % terraform apply
~省略~
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

circleci_webhook.my_webhook: Creating...
circleci_webhook.my_webhook: Creation complete after 0s [id=]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

webhooks = ""

```
<br>
<br>


無事に Webhook が作成されました！
![Screenshot 2023-04-26 at 13.54.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/0df0f673-920e-8396-056a-131df132b3f3.png)



![Screenshot 2023-04-26 at 15.16.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/020f6c75-5b1f-48ba-771c-927aa84e5284.png)


Webhookが実行され、無事にワークフローが成功したことがテーブルに記録されました。

機能の設定を変更したいときは、`main.tf` を編集後に、先ほどと同じ要領で `teffaform plan`、そして `terraform apply` を実行すれば簡単に変更できます。

また、Webhookの機能が不要になった場合、下記コマンドを実行することで削除できます。

```Terminal:Terminal
~ % terraform destroy

```


<br>
<br>

# プロジェクトの環境変数を作成・管理する

[こちら](https://registry.terraform.io/providers/kelvintaywl/circleci/latest/docs/resources/env_var)のページのコードを用いて、環境変数を作成してみました。
下記コードのうち、`"FOOBAR"` と `"FIZZBUZZ"`のところに環境変数名、その後ろに環境変数の値を入れます。
`project_slug` でプロジェクトを指定します。`VCS組織/VCS組織名/プロジェクト名` です。

```Terraform:main.tf
terraform {
  required_providers {
    circleci = {
      source = "kelvintaywl/circleci"
      version = "0.9.3"
    }
  }
}

provider "circleci" {
  // api_token = ""; or set via CIRCLE_TOKEN
  // hostname = "https://circleci.com"; or set via CIRCLE_HOSTNAME
}

# using for_each to generate multiple env var resources
resource "circleci_env_var" "my_env_vars" {
  for_each = {
    "FOOBAR"   = "0Cme2FmlXk"
    "FIZZBUZZ" = "Vbt2efixZAkrmTYiirhd"
  }

  project_slug = "github/chisato-ygc/hello-world"
  name         = each.key
  value        = each.value
}


```
<br>

先ほどと同じ要領で `teffaform plan`、そして `terraform apply` を実行します。
`Project Settings` ページで、環境変数が作成されていることを確認できました。
![Screenshot 2023-04-26 at 19.00.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/561cf5de-31a4-75b8-177e-c0462a71be7e.png)

環境変数名も変数の値も、簡単に編集できます。
![Screenshot 2023-04-26 at 19.02.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/eff6e600-7b8e-d23d-6fbb-cf111fe02ffe.png)

もちろん、`terraform destroy` で簡単に削除もできます。
![Screenshot 2023-04-26 at 19.03.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1101915/f9ccb43c-1e68-cde6-b872-e591de564350.png)



# おわりに
今回のプロジェクトのおかげで、Terraform に関する理解が以前より深まりました。
Kelvin さんには Terraform の仕組みや今回のプロジェクトについて丁寧に説明していただきました。
いつもありがとうございます。
また、プロバイダーのあまりの便利さに、さらにテクニカルな部分について学びたいという意欲が高まりました。
この記事を書いているのが4月末なので、ゴールデンウィークによってその意欲が吹き飛んでしまわないことを祈るばかりです。
がんばれ、未来の自分。

最後までお読みいただきありがとうございました。


参考URL
Terraform Documentation - Providers : [https://developer.hashicorp.com/terraform/language/providers](https://developer.hashicorp.com/terraform/language/providers)

技術的なご質問は、[サポートセンター](https://support.circleci.com/hc/ja)までお問い合わせください。