terraform importするときにちょっと便利なスクリプトを作ってみた
経緯
最近インフラ周りを触る機会が増えて、とうとうterraformまで触るようになりました。
見よう見まねでterraformコマンドを実行している時に「これって頻繁に使う割に実行するのめんどくさいし、スクリプトにしちゃえばいいのでは?」と思って作ったやつを、せっかくなので公開してみようというコンセプトの記事になります。
めちゃくちゃterraform初学者が書いてるので、もっといいやり方をご存知の方がいたら是非コメントで教えてください🙏
対象者
- terraform importの書式を毎回忘れちゃう方
- terraform import済みかどうかわからなくなってモヤモヤしたことのある方
この記事に出てくる用語
用語 | 説明 |
---|---|
HCL | terraformのコード |
ローカルリソースアドレス | terraformでリソースを定義するためのアドレス 例: module.vpc.google_compute_network.vpc
|
リソースURL | リソースの正式名みたいなもの。公式ドキュメントから確認できる 例: projects/{{project}}/locations/{{location}}/services/{{name}}
|
初学者すぎて正式な用語が使えているかわからないのですが、もし間違ってたり一般的じゃなかったりしたらコメントで教えていただけると嬉しいです、、
前提
作ったスクリプトは、以下のようなディレクトリ構成で動作しています。
もしコピペして使ってみたいという方は、ご自身の環境のディレクトリ構成に合わせて多少スクリプトの修正が必要になる場合があるのでご注意ください。
environments/
├ prod/
│ └ main.tf
└ stg/
└ main.tf
modules/
└ vpc/
└ main.tf
scripts/
└ import.sh
scripts/import.shが今回紹介するスクリプトです。
environments/prodとenvironments/stgのmain.tfは、以下のようなコードになる想定です。
provider "google" {
project = "test-project"
region = "asia-northeast1"
}
module "vpc" {
source = "../../modules/vpc"
# 省略
}
modulesのmain.tfは、以下のようなコードになる想定です。
resource "google_compute_network" "vpc" {
name = "hogehoge"
auto_create_subnetworks = false
project = "test-project"
}
本題
以下が、作ったスクリプトです。
#!/bin/bash
APP_ENV="${1:?USAGE: $0 <prod|stg>}"
PROJECT_ID='test-project'
REGION='asia-northeast1'
ZONE='asia-northeast1-a'
terraform -chdir="environments/${APP_ENV}" init
import_if_missing () {
local addr="$1"
local id="$2"
if terraform -chdir="environments/${APP_ENV}" state list | fgrep -x "$addr"; then
echo "Skipping import of $addr, already in state"
else
terraform -chdir="environments/${APP_ENV}" import "$addr" "$id"
fi
}
# import_if_missing()の使い方の例
import_if_missing module.vpc.google_compute_network.vpc "projects/${PROJECT_ID}/global/networks/hogehoge"
メインはimport_if_missing()
関数です。
この関数が、terraform importを実行しています。
実行する時は以下のようにします。
# environments/prodに対して実行したい時
$ . scripts/import.sh prod
# environments/stgに対して実行したい時
$ . scripts/import.sh stg
実行結果は以下のような感じになると思います。
$ . scripts/import.sh prod
Initializing the backend...
Initializing modules...
Initializing provider plugins...
- Reusing previous version of hashicorp/google from the dependency lock file
- Reusing previous version of hashicorp/google-beta from the dependency lock file
- Using previously-installed hashicorp/google v6.38.0
- Using previously-installed hashicorp/google-beta v7.1.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.
# ↓importされてなかったリソース(terraform importを実行してる)
module.load_balancer.google_compute_region_network_endpoint_group.cloud_run_neg_ws: Import prepared!
Prepared google_compute_region_network_endpoint_group for import
module.load_balancer.google_compute_region_network_endpoint_group.cloud_run_neg_ws: Refreshing state... [id=******]
data.google_project.project: Read complete after 2s [id=******]
# ↓import済みだったリソース(スキップされてる)
module.vpc.google_compute_network.vpc
Skipping import of module.vpc.google_compute_network.vpc, already in state
このスクリプトは何をしているの?
上から見ていきます。
APP_ENV="${1:?USAGE: $0 <prod|stg>}"
PROJECT_ID="test-project"
REGION='asia-northeast1'
ZONE='asia-northeast1-a'
ここは、環境変数を設定しています。
APP_ENV
は、スクリプト実行時の引数を使うので、${1:?USAGE: $0 <prod|stg>}
として必ずprodかstgのどちらかが来るようにしています。
terraform -chdir="environments/${APP_ENV}" init
ここでterraform init
しています。
-chdir
は、コマンドを実行するディレクトリを指定しています。
terraform init
についての解説はここではしませんので、初心者の方は公式ドキュメントなどを読んでみてください。
これ以降に出てくるterraformコマンドについても同様に解説はしませんので、ご了承ください。
import_if_missing () {
local addr="$1"
local id="$2"
if terraform -chdir="environments/${APP_ENV}" state list | fgrep -x "$addr"; then
echo "Skipping import of $addr, already in state"
else
terraform -chdir="environments/${APP_ENV}" import "$addr" "$id"
fi
}
先ほども書きましたが、ここがこのスクリプトの肝です。
ここでは、「まだterraform import
されていないリソースにのみterraform import
を実行し、そうでなければスキップする」という関数の定義をしています。
以下は、解説コメントを入れたバージョンの同じ関数です。
import_if_missing () {
local addr="$1" # ローカルリソースアドレス
local id="$2" # リソースURL
# `terraform state list`コマンドでimport済みのローカルリソースアドレス一覧を表示し、
# その中に$addrと同じものがあればimport済みなのでスキップ
if terraform -chdir="environments/${APP_ENV}" state list | fgrep -x "$addr"; then
echo "Skipping import of $addr, already in state"
else
terraform -chdir="environments/${APP_ENV}" import "$addr" "$id"
fi
}
実際に何度かterraform import
したことある方はご存知だと思いますが、すでにimport済みのリソースに対して実行してしまうと「もうimportされてるよ!」というエラーが出ます。
どれをimportしたか覚えてたら問題ないんですが、管理するリソースの数が増えたり減ったりしてガチャガチャやっているうちに大体よくわからなくなります。
こういう時、terraform state list
というコマンドを実行すると、現時点でimportされているリソースの一覧が確認できるんですが、そもそもリソースの数が多いと探すのも大変です。
エラーが気にならないなら何度でもterraform import
すれば済む話ですが、個人的何度もそれをやってるとウザくなってきてしまうんですね、、
そこで、
「import済みか確認」→「importされてなければterraform import
を実行」or「import済みならスキップ」
という流れを自動化できたら楽だなと思いました。
その後は簡単で、terraform state list
の実行結果に対してローカルリソースアドレスでfgrep
すればimport済みかどうか判定できるので、それを利用した条件分岐でいい感じに自動化できたというところです。
ちなみに、現在の僕の環境では、import.shは以下のようになっています。
管理してるリソースの数が多いので、素でterraform import
してると結構しんどかったです。
最後に
もっと便利にもできるかなと思うんですが、個人的にはこれくらいシンプルな方が小回りがきいて使いやすいよな〜というのが念頭にあったので、こういったスクリプトになりました。
本文ではスクリプトを普通に実行することだけ紹介しましたが、僕は普段terraform apply -target=hogehoge
のように-target
オプションを使いたい時に、hogehoge
の部分(ローカルリソースアドレス)のコピペ用としても使ってます。

株式会社 カラビナテクノロジーは「命綱や支点を素早く確実に繋ぐカラビナ。そんなカラビナのような役割をテクノロジーで実現したい」という想いのもと、福岡で設立。 主にシステム開発・アプリ開発・ Webサイト制作を行っています。採用情報→karabiner.tech/career
Discussion