はじめに

Herokuで実際にアプリケーションを運用していると機能の追加に伴って環境変数を追加したり、アドオンを追加したりすることがあると思います。
そして、これらは手作業で行うことが多いです。ごく少人数で開発しているときはそれでもなんとかなるのですが、開発者が増えたり、開発者のレベルにばらつきが出てくるとコードで管理してリリースする前にレビューを行いたいと思うようになってきました。
現在developer previewで公開されているheroku.ymlというマニフェストファイル的な機能がゆくゆくはそういうものになっていってほしいなあと個人的に考えているのですが、現状はどちらかというとアプリケーションのbuildをシンプルに記述できるようにするのが目的のようなので、本番環境で参照する環境変数を管理したり、アドオンを管理したりする機能はありません。となると、現状で私のニーズを満たしてくれる最有力候補はTerraformになりそうです。
前置きが長くなりましたが、Terraformを試してみたいと思います。

今回のゴール

  • Terraformを利用して新規にHerokuアプリケーションをセットアップしてみる。

実際にやってみる

前提

  • 開発環境はMacです。
  • すでにHerokuのアカウントは開設済みでheroku login済みとします。

Terraformのインストール

  • Homebrewを利用します。
brew install terraform

tfファイルを書く

  • アプリケーションとheroku-postgresqを用意するだけのシンプルなものです。
variable "heroku_api_key" {}

provider "heroku" {
  email   = "your.email@example.com"
  api_key = "${var.heroku_api_key}"
}

resource "heroku_app" "default" {
  name   = "sugai-tf-test"
  region = "us"
}

resource "heroku_addon" "database" {
  app  = "${heroku_app.default.name}"
  plan = "heroku-postgresql:hobby-dev"
}

初期化

  • いろいろ調べているとterraform planがdry run(試しに実行してリソースへの変更は加えない)でterraform applyが実際に変更を適用する、ということを知りました。
  • というわけで上記のファイルを用意した段階でterraform planしてみたところ下記のようなメッセージが。
$ terraform plan
Plugin reinitialization required. Please run "terraform init".
Reason: Could not satisfy plugin requirements.

Plugins are external binaries that Terraform uses to access and manipulate
resources. The configuration provided requires plugins which can't be located,
don't satisfy the version constraints, or are otherwise incompatible.

1 error(s) occurred:

* provider.heroku: no suitable version installed
  version requirements: "(any version)"
  versions installed: none

Terraform automatically discovers provider requirements from your
configuration, including providers used in child modules. To see the
requirements and constraints from each module, run "terraform providers".


Error: error satisfying plugin requirements
  • どうやらterraform initで初期化する必要があるようです。
  • ドキュメントにも書いてありました。
  • というわけでterraform initしてみます。
$ terraform init

Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "heroku" (0.1.2)...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.heroku: version = "~> 0.1"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
  • 問題なさそうですね。

次にterraform planしてみる

  • 初期化ができたので続いてterraform planしてみます。
$ terraform plan
var.heroku_api_key
  Enter a value: xxxxxxxxxxxxxxx

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + heroku_addon.database
      id:                <computed>
      app:               "sugai-tf-test"
      config_vars.#:     <computed>
      name:              <computed>
      plan:              "heroku-postgresql:hobby-dev"
      provider_id:       <computed>

  + heroku_app.default
      id:                <computed>
      all_config_vars.%: <computed>
      config_vars.#:     <computed>
      git_url:           <computed>
      heroku_hostname:   <computed>
      name:              "sugai-tf-test"
      region:            "us"
      stack:             <computed>
      web_url:           <computed>


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

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
  • なお、HerokuのAPIキーは下記のコマンドで取得できます。
heroku auth:token

最後にterraform applyする

  • 特に問題なさそうなのでいよいよterraform applyを実行します。
$ terraform apply
var.heroku_api_key
  Enter a value: xxxxxxxxxxx


An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + heroku_addon.database
      id:                <computed>
      app:               "sugai-tf-test"
      config_vars.#:     <computed>
      name:              <computed>
      plan:              "heroku-postgresql:hobby-dev"
      provider_id:       <computed>

  + heroku_app.default
      id:                <computed>
      all_config_vars.%: <computed>
      config_vars.#:     <computed>
      git_url:           <computed>
      heroku_hostname:   <computed>
      name:              "sugai-tf-test"
      region:            "us"
      stack:             <computed>
      web_url:           <computed>


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

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

heroku_app.default: Creating...
  all_config_vars.%: "" => "<computed>"
  config_vars.#:     "" => "<computed>"
  git_url:           "" => "<computed>"
  heroku_hostname:   "" => "<computed>"
  name:              "" => "sugai-tf-test"
  region:            "" => "us"
  stack:             "" => "<computed>"
  web_url:           "" => "<computed>"
heroku_app.default: Creation complete after 2s (ID: sugai-tf-test)
heroku_addon.database: Creating...
  app:           "" => "sugai-tf-test"
  config_vars.#: "" => "<computed>"
  name:          "" => "<computed>"
  plan:          "" => "heroku-postgresql:hobby-dev"
  provider_id:   "" => "<computed>"
heroku_addon.database: Creation complete after 2s (ID: 88eaebdb-4b52-4b0e-853a-6fef33b70012)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
  • 途中で「ほんとに実行して良い?」と聞かれるのでyesと入力すると変更が適用されます。
  • Herokuの管理画面で見てみると、たしかに新規のアプリケーションが作成されています。簡単。

Heroku管理画面

おわりに

驚くほど簡単にTerraformを使って新たにHerokuアプリケーションを立ち上げることができました。
ごくシンプルなアプリケーションの雛形だということもあるとは思いますが、それでも拍子抜けするくらい簡単でした。
ただこういうツールって既存の稼働中のプロジェクトに導入して実際に運用してみる中で得られる知見に価値があるのかなと思うので、近いうちに個人で運用しているHerokuアプリケーションで試してみたいと思います。

参考

  • Provider: Heroku - Terraform by HashiCorp
  • TerraformでHerokuアプリのセットアップ | SOTA