Slack AppをTerraformで管理してみよう

みなさん [Slack App](https://api.slack.com/start) 使ってますか？ 
色々な業務を Slack 上で完結できるようになってとても便利ですよね．
自分もCI/CDや業務効率化のツールの一部として利用しています．


そんな便利な Slack App ですが，自分は今まで手オペで管理していたため，いざ新たにアプリを作る際に困ることが多かったです．

 * 設定項目ぽちぽちするのが面倒
 * Slack App 以外のリソース (AWS Lambdaなど) を別途用意する必要があり面倒

今回，そういった問題を解決するべく Slack App を Terraform で管理するようにしてみたので紹介します．

利用したのは下記の仕組みです．
 * Slack App Manifest
 * Terraform null_resource

順々に説明し最後に手順をまとめます．

## [Slack App Manifest](https://api.slack.com/reference/manifests)
Slack App の定義をJsonやYAML形式で記述することが可能な仕組みです．
利用方法としては下記の２種類があります．
 * WebのUIから利用する
 *  [API](https://api.slack.com/reference/manifests#manifest_apis) をcurlなどで直接叩いて利用する

今回は Terraform 経由で利用したいため，API を利用していきます．
定義に使う Manifest のスキーマは[こちらのページ](https://api.slack.com/reference/manifests#fields)から確認が可能です．

## [Terraform null_resource](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource)
何もしない Terraform resource です．他の Terraform resource と同じライフサイクルで管理されるため，なにがしかのリソースを作成したきっかけに何かしたい時に便利です．

例えば， null_resource と [local-exec Provisioner](https://developer.hashicorp.com/terraform/language/resources/provisioners/local-exec) を併用することでシェルの実行が可能になります．
今回はこの仕組みを利用して Manifest API を叩いて Slack App を作成してみます．

## 手順

### 1. アクセストークンの取得

まず Manifest API を利用するためのアクセストークンを取得します．
アクセストークンは[こちらのページ](https://api.slack.com/authentication/config-tokens)から取得が可能です．
(※ アクセストークンの有効期限は12時間なので，同じページからトークンの更新 (Refresh) を行なってください)


### 2. Manifest の作成
アプリの設定である Manifest を作成していきます．
今回はスラッシュコマンド機能を持った Slack App を作る例です．
他に定義可能な値は[公式ページ](https://api.slack.com/reference/manifests#fields)を参照ください．

```text:app_manifest.json
{
  "manifest": {
    "display_information": {
      "name": "${app_name}",
      "description": "${description}",
    },
    "features": {
      "bot_user": {
        "display_name": "${bot_name}"
      },
      "slash_commands": ${jsonencode(slash_commands)}
    },
    "oauth_config": {
      "scopes": {
        "bot": ${jsonencode(bot_scopes)}
      },
    }
  }
}

```

### 3. Terraform 側の設定
Slack App用の Terraform resource を定義していきます．
重要なところは null_resource の部分で local-exec Provisioner を使って curl を叩いています． 
手順１で取得したアクセストークンを誤ってコミットしてしまうとまずいので `TF_VAR_access_token` などの環境変数経由で渡すようにしてあげてください．

```hcl:main.tf
variable app_name {
  type = string
}

variable bot_name {
  type = string
}

variable description {
  type = string
}

variable bot_scopes {
  type = list(string)
}

variable slash_commands {
  type = list(map(string))
}

// access_token は TF_VAR_access_token の環境変数経由とかで渡してください
variable access_token {
  type      = string
  sensitive = true
}

locals {
  template = templatefile(
    "templates/app_manifest.json.tftpl",
    {
      app_name       = var.app_name
      bot_name       = var.bot_name
      description    = var.description
      slash_commands = var.slash_commands
      bot_scopes     = var.bot_scopes
    }
  )
}

## メインのリソース．curl を叩いててリクエストボディに app_manifest.json を渡している
resource "null_resource" "slack_app" {
  provisioner "local-exec" {
    command = "curl -XPOST -H \"Authorization: Bearer ${var.access_token}\" -H \"Content-type: application/json\" -d '${local.template}' https://slack.com/api/apps.manifest.create"
  }
}
```

* tfvarsをよしなに定義
```text:main.tfvars
app_name    = "test_app"
bot_name    = "test_app_user"
description = "test app"
bot_scopes  = [
  "channels:read",
  "chat:write",
  "chat:write.public",
  "commands"
]
slash_commands = [
  {
    "command" : "/hoge",
    "description" : "hoge",
    "usage_hint" : "/hoge echo hoge",
    "url" : "https://example.com/api/hoge"
  }
]
```

### 4. Terraform apply
bashなどで `terraform apply -var-file main.tfvars`  を実行

## 終わりに
Slack App を Terraform で管理する方法について説明しました．
Terraform で管理することで周辺のリソースまで一気通貫で作れて良いですね．
似た構成のアプリを作るときの手間がだいぶ減りそうです．

ただ，アクセストークンが12時間毎に切り替わるため，Github ActionsなどのCICDツールとの組み合わせは難しいです．
また，今回は手軽さ重視で null_resource から bash 実行する方法を紹介しましたが更新や削除はできません．．．
Slack App 用の provider を自作すればそれも可能になりますがまたの機会ということで．

それでは良きSlack Appライフを！