terraformのmoduleの基本的な使い方を試しました
あれもこれもやらなくては~と思っていたらどれにも手がついておらず結構ガッカリですが、ひとまず今日はterraformのmoduleの基本についてまとめておきます。
TL;DR
- terraformがビルドに使うのはカレントディレクトリにある.tfファイル
- moduleで他のディレクトリや他のサーバにある設定をコピーしてきて再利用する
- moduleのsourceで指定するコピー元がローカルファイルでも
terraform plan
の前にterraform init
必要
- moduleのsourceで指定するコピー元がローカルファイルでも
- variableを利用してmoduleを書いた側で一部の変数を代入する
terraformがビルドに使うのはカレントディレクトリにある.tfファイル
terraformが terraform plan
や terraform apply
した時に利用する設定ファイル(.tf
)はterraformのコマンドを実行したディレクトリとなります。
たとえば、
$ tree . . ├── foo │ └── test_3.tf ├── test.tf └── test_2.tf
// test.tf provider "aws" { access_key = "HOGEHOGE" secret_key = "FUGAFUGA" region = "ap-northeast-1" } resource "aws_instance" "hoge" { ami = "ami-29160d47" instance_type = "t2.nano" }
// test_2.tf resource "aws_instance" "fuga" { ami = "ami-29160d47" instance_type = "t2.nano" }
// test_3.tf resource "aws_instance" "bar" { ami = "ami-29160d47" instance_type = "t2.nano" }
というようなディレクトリ構成のとき、カレントディレクトリで terraform plan
すると結果に出てくるリソースは
$ terraform plan 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: + aws_instance.fuga id: <computed> ami: "ami-29160d47" arn: <computed> ... + aws_instance.hoge id: <computed> ami: "ami-29160d47" arn: <computed> ...
のように aws_instance.fuga
, aws_instance.hoge
の2つだけです。
他のディレクトリのファイルの設定を利用する場合は、次に説明する module
を利用します。
moduleで他のディレクトリや他のサーバにある設定をコピーしてきて再利用する
moduleを使うと、
module "test" { source = "./modules/test" }
のように、参照元の設定ファイルがあるディレクトリを指定して、参照元に書かれた設定を他のディレクトリで利用することができます。
たとえば、先ほどの設定ファイルに対し、
// test.tf provider "aws" { access_key = "HOGEHOGE" secret_key = "FUGAFUGA" region = "ap-northeast-1" } resource "aws_instance" "hoge" { ami = "ami-29160d47" instance_type = "t2.nano" } module "foo" { source = "./foo" }
のように module
の部分を追加すると、terraform plan の結果に foo/test_3.tf
に書かれていた aws_instance.bar
が module.foo.aws_instance.bar
として登場するようになります。
$ terraform plan 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: + aws_instance.fuga id: <computed> ami: "ami-29160d47" arn: <computed> ... + aws_instance.hoge id: <computed> ami: "ami-29160d47" arn: <computed> ... + module.foo.aws_instance.bar id: <computed> ami: "ami-29160d47" arn: <computed> ...
ここで、 test_2.tf
にも同じ設定を追記すると
$ terraform plan Error: Failed to load root config module: Error loading modules: module foo: duplicated. module names must be unique
となります。
設定側で module
に対して一意になるように名前をつけるのは、おそらく module
が、どこかにある設定を利用する側の都合でコピーしてきたり、コピーしてきた設定に一部変数を代入して使うためのもののようだからと思います。
先ほどまで試していたローカルファイルシステム上の他のディレクトリは、 module
の source
として指定できるデータの取得元の一部で、 https://www.terraform.io/docs/modules/sources.html によると、他には
- Terraform Registry (Docker HubみたいにTerraformの設定をアップロードできるサイト)
- GitHub
- BitBucket
- 他のgitリポジトリ
- 特定のサイトのURL
- S3のバケット
があります。
そういえば、この取得元の一覧を見ていると module
で外部ソースを指定した時は git fetch
ではないんですが、何かコマンドを打たなければいけないような気がします。
じつは、新しく module
を設定に追加するとその module
のソースがローカルファイルでも一度 terraform init
を打つ必要があります。
terraform init
を忘れて terraform plan
を実行したときのログと、その後 terraform init
を打って、./foo
以下から module foo
の設定ファイルを取得している様子のログが以下となります。
$ terraform plan Error: Failed to load root config module: Error loading modules: module foo: not found, may need to run 'terraform init' $ terraform init Initializing modules... - module.foo Getting source "./foo" Initializing provider plugins... The following providers do not have any version constraints in configuration, so the latest version was installed.
variableを利用してmoduleを書いた側で一部の変数を代入する
さて、moduleの使い方について「module
がどこかにある設定を利用する側の都合でコピーしてきたり、コピーしてきた設定に一部変数を代入して使うためのもののようだ」と書きましたが、「コピーしてきた設定に一部変数を代入して使うためのもののようだ」の方についてももう少し詳しく見ておくことにします。
variables
については今度 local
と並べて使える型と使う場面の違いについてまとめようかな、と思いますが、terraformには
variable "hogehoge" {}
のように変数があることを宣言して、設定ファイル中で
${var.hogehoge}
のように利用できる変数 variables
があります。各 variables
の変数の値は、
- 設定ファイル中で記載したり
- 別の設定ファイルに抜き出してコマンドの実行時に
-var-file=foo
のように変数が書かれたファイルを指定したり - コマンドの実行時に
-var 'foo=bar'
のように一つずつ設定したり
できます。
設定ファイル中で記載できる箇所には module
の中もあり、これを利用して
module
でsource
に指定する設定ファイル: 可変にしたい場所は${var.env}
のようにvariable
を利用して書いておくmodule
を定義する側:module
のブロックの中で、source
の設定ファイルに書かれたvariable
の値を設定する
のようにすると、production, staging環境で一部だけ設定が違う、というような場合の記述が便利にできます。
簡単なサンプルを書いてためしておきましょう。
$ tree . . ├── modules │ └── ec2.tf ├── production │ └── main.tf └── staging └── main.tf
// ./modules/ec2.tf // stagingとproductionでAWSアカウントを分けてないとする provider "aws" { access_key = "HOGEHOGE" secret_key = "FUGAFUGA" region = "ap-northeast-1" } variable "environment" {} resource "aws_instance" "app" { ami = "ami-29160d47" instance_type = "t2.nano" tags { Name = "app-${var.environment}" env = "${var.environment}" } }
// ./production/main.tf module "app_production" { source = "./../modules" environment = "production" }
// ./staing/main.tf module "app_staging" { source = "./../modules" environment = "staging" }
production$ terraform init production$ terraform plan ... + module.app_production.aws_instance.app id: <computed> ami: "ami-29160d47" arn: <computed> ... instance_type: "t2.nano" ... tags.Name: "app-production" tags.env: "production"
staging$ terraform init staging$ terraform plan ... + module.app_staging.aws_instance.app id: <computed> ami: "ami-29160d47" arn: <computed> ... instance_type: "t2.nano" ... tags.Name: "app-staging" tags.env: "staging"
現場からは以上です。