松田です!

寒すぎる!
ダウンはじめました!

今日はTerraformネタです!
Terraformerなら何千/何万回と実行するterraform plan/applyですが、ファイル単位で実行したいというケースはありませんか?
terraform plan/applyにtargetオプションがあるので、リソース単位で実行することは可能です。ただファイル単位で実行するオプションは用意されていません。

快適なterraform生活を送るためのTipsとして、terraform plan/applyをファイル単位で(スマートに)実行する方法について紹介します。

結論から知りたい人は「結論」から読んでください。

んで、どうやるの?

terraform plan/applyにはファイル単位で対象を指定するオプションがありません。対象を指定するにはtargetオプションでリソースやモジュールを指定するしかありません。
なので、対象のファイル内に定義されているリソースやモジュールを取得して、それをtargetオプションの引数に渡してあげると実現できそうです。

リソースとモジュールをフィルタリング

cat sample.tf | grep -E 'resource |module ' 

このコマンドを実行すると、以下のように指定したファイル内のリソースとモジュールだけを取得できます。

resource "aws_vpc" "sample" {
resource "aws_subnet" "sample" {
resource "aws_route_table" "sample_public" {
resource "aws_route_table" "sample_protected" {
resource "aws_route_table" "sample_private" {

apply/planコマンドに渡せる形に整形

targetオプションの引数は1つのリソース/モジュールを指定することができます。複数のリソース/モジュールを指定する場合は-target "aws_vpc.sample -target "aws_subnet.sample"といった感じでtargetオプションを必要な数呼び出します。またtargetオプションの引数となるリソースとモジュールはダブルクォーテーションで囲う必要があります。
ではgrepの実行結果をtargetオプションに渡せる形に直します。

まずtr -d '"'でダブルクォーテーションを取りのぞきます。

cat sample.tf | grep -E 'resource |module ' | tr -d '"'

すると

resource aws_vpc sample {
resource aws_subnet sample {
resource aws_route_table sample_public {
resource aws_route_table sample_protected {
resource aws_route_table sample_private {

となります。

さらにawk '{printf("-target=%s.%s ",$2,$3);}'でフォーマットすると

cat sample.tf | grep -E 'resource |module ' | tr -d '"' | awk '{printf("-target=%s.%s ",$2,$3);}' 

以下のようにapply/planコマンドに渡せる形になります。

-target=aws_vpc.sample -target=aws_subnet.sample -target=aws_route_table.sample_public -target=aws_route_table.sample_protected -target=aws_route_table.sample_private

apply/planコマンドに渡す

cat sample.tf | grep -E 'resource |module ' | tr -d '"' | awk '{printf("-target=%s.%s ",$2,$3);}' 

上記コマンドの結果をterraform apply/planに渡せば良いので、

terraform plan $(cat sample.tf | grep -E 'resource |module ' | tr -d '"' | awk '{printf("-target=%s.%s ",$2,$3);}')

これでファイルを指定でterraform plan/applyを実行することができます。

結論

以下コマンドでterraform plan/applyをファイル単位で実行することができます。

# plan
terraform plan $(cat sample.tf | grep -E 'resource |module ' | tr -d '"' | awk '{printf("-target=%s.%s ",$2,$3);}')

# apply
terraform apply $(cat sample.tf | grep -E 'resource |module ' | tr -d '"' | awk '{printf("-target=%s.%s ",$2,$3);}')

スマートに

上のコマンドをターミナルにコピペして、ファイル名だけ変更して….とやるのはダサい面倒なので、スマートに実行できるようにします。

以下を.zshrcなり.bashrcなりに登録してください。

# Terraform Plan per File
function tpf() {
  if [ $# -eq 0 ]; then
    echo "terraform plan per file"
    echo "Usage: tpf <filename.tf>"
    return 1
  fi
  terraform plan $(cat $1 | grep -E 'resource |module ' | tr -d '"' | awk '{printf("-target=%s.%s ",$2,$3);}')
}

# Terraform Apply per File
function taf() {
  if [ $# -eq 0 ]; then
    echo "terraform apply per file"
    echo "Usage: taf <filename.tf>"
    return 1
  fi
  terraform apply $(cat $1 | grep -E 'resource |module ' | tr -d '"' | awk '{printf("-target=%s.%s ",$2,$3);}')
}

これで

# plan
tpf sample.tf

# apply
taf sample.tf

といったようにスマートに実行することができます。

最後に

結局のところシェル芸でゴリ押しですが、関数化でそれを隠してスマートにファイル単位でterraform plan/applyを実行しましょう!
以上です!