Terraformの学習も、少しずつ「AWSリソースを作る」だけではなくなってきました。
前回は Variables を学びました。
Variablesでは、
«Terraformの外から設定値を渡す»
という考え方を学びました。
今回登場するのは、その反対側にある機能です。
それが、
Outputs
です。
今日の実務シナリオ
Terraformを実行したとします。
terraform apply
するとAWSには、
- ALB
- EC2
- RDS
- VPC
などが作成されました。
ところが……
👨💼 上司
«「ALBのURL教えて。」»
😨
「どこで見るんだ……?」
AWSコンソールを開いて、
EC2
↓
ALB
↓
DNS name
と探すこともできます。
でも、毎回これをやるのは面倒です。
そこで登場するのが、
Output
です。
今日覚えるコード
例えばALBのDNS名を表示したい場合、
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
と書きます。
今回まず覚えるのは、
output
と、
value
です。
Outputすると何が起きる?
例えば、
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
と書いて、
terraform apply
を実行すると、Terraformの実行結果として、
alb_dns_name = sample-alb-123456.ap-northeast-1.elb.amazonaws.com
のように確認できます。
つまり、
Terraform
↓
AWSリソースを作る
↓
作ったリソースが持っている情報を取得
↓
Output
↓
必要な情報を確認
という流れです。
ここで重要なこと
最初は、
«Output = 作ったAWSサービスを表示する機能»
と思ってしまいやすいです。
でも、これは少し違います。
Outputは、
«AWSサービスそのものではなく、そのリソースが持っている「必要な情報」を取り出す機能»
と考えると分かりやすいです。
例えばALBの場合
ALBというAWSリソースがあります。
ALB
これだけ表示されても、
👨💼 上司
«「ALBがあるのは分かった。URLは?」»
となります。
欲しいのはALBそのものではありません。
欲しいのは、
DNS Name
です。
そこで、
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
とします。
すると、
ALB
↓
DNS Name
↓
sample-alb-123456.ap-northeast-1.elb.amazonaws.com
という情報を取り出せます。
"value" は何を指定している?
ここが今回かなり重要です。
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
この中の、
value
は、
«Outputとして何の値を出すのか»
を指定しています。
つまり、
output
↓
「何を出す?」
↓
value
↓
「ALBのDNS名を出して!」
というイメージです。
コードを分解してみる
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
これを分解すると、
output
↓
Outputを定義する
"alb_dns_name"
↓
Outputにつける名前
value
↓
何の値を出すか
aws_lb.alb.dns_name
↓
ALBのDNS名
となります。
AWSリソースと「属性」を区別する
ここが今回一番重要なポイントです。
例えば、
aws_lb.alb
は、
«ALBというリソース»
です。
一方、
aws_lb.alb.dns_name
は、
«そのALBが持っているDNS名という属性»
です。
同じように、
aws_db_instance.main.endpoint
なら、
«RDSインスタンスが持っているEndpoint»
です。
aws_vpc.main.id
なら、
«VPCが持っているVPC ID»
です。
つまり、
AWSリソース
↓
そのリソースが持っている情報
↓
必要なものをOutputする
という考え方です。
イメージしやすく考えてみる
AWSサービスを「人」だと考えてみます。
例えば、
EC2さん
という人がいたとします。
その人には、
名前
住所
電話番号
社員番号
など、いろいろな情報があります。
でも、
«「EC2さんを表示して!」»
と言われても、何を知りたいのか分かりません。
知りたいのは、
EC2
↓
Instance ID
かもしれないし、
EC2
↓
Private IP
かもしれません。
Outputは、
«そのリソース全部を表示するのではなく、必要な情報を指定して取り出す»
というイメージです。
今日の問題
Q1
なぜ、
output
を書くのでしょうか?
Q2
なぜ、
value
を書くのでしょうか?
何を指定していると思いますか?
⭐ 今日一番重要
会社でTerraformを実行したとします。
すると、
ALB
VPC
RDS
EC2
など、全部作成されました。
でも上司から、
«「RDS Endpointだけ教えて。」»
と言われました。
このとき、
Outputがあると便利なのはなぜでしょうか?
🎁 最後に応用問題
今まで勉強してきた中で、
«Outputしたら便利そうなものを3つ»
挙げてみてください。
例えば、
ALBのURL
のように、
«「作ったあとによく確認したい情報」»
を考えてみましょう。
今回の回答
Q1
«Terraformで作った情報を共有するときにしやすくなるため。»
Q2
«"output "alb_dns_name" {" にValueを書くことでALBの値(URL)を示せるから。»
⭐ 今日一番重要
«RDSのデータだけを共有できるから。»
🎁 Outputしたら便利なもの
«EC2、RDS、VPC»
今回のレビュー
Q1:なぜOutputを書く?
回答
«Terraformで作った情報を共有するときにしやすくなるため。»
評価:💯
正解です。
より正確に言うなら、
«Terraformで作ったリソースの必要な情報を、Terraformの実行後に確認しやすくするため»
です。
例えば、
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
と書いておけば、
terraform apply
の後にALBのDNS名を確認できます。
AWSコンソールを開いて探しに行く必要がありません。
Q2:なぜ "value" を書く?
回答
«"output "alb_dns_name" {" にValueを書くことでALBの値(URL)を示せるから。»
評価:95点
方向性は合っています。
ただし、ここはもう一段正確に理解しておきましょう。
"value" は、
«Outputとして何の値を出すのかを指定する»
ものです。
例えば、
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
なら、
value
↓
aws_lb.alb.dns_name
↓
ALBのDNS名を出す
となります。
ここで大事なのは、
«valueは「ALBのURL専用」ではない»
ということです。
例えば、
output "vpc_id" {
value = aws_vpc.main.id
}
でもいい。
output "rds_endpoint" {
value = aws_db_instance.main.endpoint
}
でもいい。
output "ec2_private_ip" {
value = aws_instance.web.private_ip
}
でもいい。
つまり、
«"value" に「Outputしたい値」を指定する»
と覚えればOKです。
⭐ 今日一番重要な問題のレビュー
回答
«RDSのデータだけを共有できるから。»
評価:💯
これも考え方として正解です。
ただし、「RDSのデータ」というより、
«RDSの必要な情報»
と表現するとさらに正確です。
例えば、
output "rds_endpoint" {
value = aws_db_instance.main.endpoint
}
とすると、
RDS
↓
Endpoint
↓
sample-db.xxxxxx.ap-northeast-1.rds.amazonaws.com
という情報を確認できます。
上司から、
«「RDS Endpointだけ教えて。」»
と言われたときに、
AWSコンソールを開いて探すのではなく、
terraform apply
↓
Output
↓
RDS Endpoint
と確認できるわけです。
これが実務で便利なポイントです。
🎁 Outputしたら便利なもの
今回の回答は、
«EC2、RDS、VPC»
でした。
ここは少しだけ修正があります。
実はOutputするのは、
EC2そのもの
でも、
RDSそのもの
でも、
VPCそのもの
でもありません。
Outputするのは、
«そのリソースの中にある「知りたい情報」»
です。
例えばALB
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
取り出すのは、
ALB
↓
DNS Name
です。
これは、
«ブラウザからWebサービスへアクセスするとき»
などに便利です。
RDSなら
output "rds_endpoint" {
value = aws_db_instance.main.endpoint
}
取り出すのは、
RDS
↓
Endpoint
です。
これは、
«アプリケーションからRDSへ接続するとき»
などに必要になります。
VPCなら
output "vpc_id" {
value = aws_vpc.main.id
}
取り出すのは、
VPC
↓
VPC ID
です。
VPC IDは、
- 別のTerraform構成
- Module
- AWSリソースの設定
などで必要になることがあります。
EC2なら何をOutputする?
ここで少し考えてみましょう。
EC2を作ったとして、
resource "aws_instance" "web" {
...
}
Outputするとしたら、何が便利でしょうか?
例えば、
Instance ID
output "ec2_instance_id" {
value = aws_instance.web.id
}
あるいは、
Private IP
output "ec2_private_ip" {
value = aws_instance.web.private_ip
}
などがあります。
場合によっては、
Public IP
Private DNS
などをOutputすることもあります。
つまり、
«「EC2をOutputする」»
ではなく、
«「EC2の何が必要なのか?」»
と考えることが重要です。
ここで大事な気づき
今回、少し面白いやり取りがありました。
「EC2からRDSへ通信するための情報をOutputしたい」
という考え方です。
これはかなり良い発想です。
なぜなら、
«「何をOutputできるか」ではなく、「誰が何のためにその情報を使うのか」»
を考えているからです。
例えば、
EC2
↓
RDSへ接続したい
↓
何の情報が必要?
↓
RDS Endpoint
となります。
だから、
output "rds_endpoint" {
value = aws_db_instance.main.endpoint
}
という設計につながります。
これは単なるTerraformの文法ではありません。
システム設計の考え方です。
Outputは「表示機能」だけではない
ここも少し発展的な話です。
Outputというと、
«Terraformを実行したら画面に表示されるもの»
というイメージを持ちやすいです。
もちろん、それも正しいです。
例えば、
terraform apply
↓
ALB DNS
RDS Endpoint
VPC ID
などを確認できます。
しかし、Outputにはもう一つ重要な役割があります。
それが、
«別のTerraform構成などへ、作成したリソースの情報を受け渡す»
という使い方です。
例えばVPCを別の構成から使いたい
例えば、
Terraform A
↓
VPCを作成
↓
VPC ID
↓
Terraform B
↓
EC2などを作成
という構成を考えます。
Terraform Aで、
output "vpc_id" {
value = aws_vpc.main.id
}
としておけば、
Terraform A
│
│ VPC ID
↓
Terraform B
という情報の受け渡しにつなげられます。
実際に別のTerraform構成からOutputを参照する場合は、Remote Stateなどの仕組みを使うことがあります。
そのため、
«Output = 画面に表示するだけ»
と覚えてしまうと少しもったいないです。
より正確には、
«Terraformで作成した重要な情報を外へ公開するためのインターフェース»
と考えることができます。
VariablesとOutputsをセットで考える
ここで前回のVariablesとつながります。
Variablesは、
外部
↓
Terraform
という方向でした。
例えば、
variable "instance_type" {
default = "t3.micro"
}
です。
一方、Outputsは、
Terraform
↓
外部
という方向です。
例えば、
output "vpc_id" {
value = aws_vpc.main.id
}
です。
図にすると、
Terraform
│
┌─────────┴─────────┐
↓ ↓
Variables Outputs
↓ ↓
値を受け取る 値を外へ出す
この関係を理解すると、Terraformの設計がかなり見やすくなります。
今回覚えること
まずはこの形を覚えましょう。
output "名前" {
value = 取り出したい値
}
例えばALBなら、
output "alb_dns_name" {
value = aws_lb.alb.dns_name
}
RDSなら、
output "rds_endpoint" {
value = aws_db_instance.main.endpoint
}
VPCなら、
output "vpc_id" {
value = aws_vpc.main.id
}
です。
Outputを考えるときの質問
今後Outputを書くときは、
«「このリソースを作ったあと、誰が何を知りたい?」»
と考えてみましょう。
例えば、
ALB
誰が?
↓
開発者・利用者
何を?
↓
アクセス先
Output
↓
DNS Name
RDS
誰が?
↓
アプリケーション・開発者
何を?
↓
接続先
Output
↓
Endpoint
VPC
誰が?
↓
別のTerraform構成・AWSリソース
何を?
↓
VPCの識別情報
Output
↓
VPC ID
この考え方ができれば、
「Outputには何を書けばいいんだ?」
と迷いにくくなります。
🌟 今回の理解度
⭐⭐⭐⭐⭐ 5 / 5
Outputは、これまで学んできた、
- VPC
- Subnet
- ALB
- EC2
- RDS
- Security Group
のような「AWSリソース」とは少し性質が違います。
そのため、最初に難しく感じるのは自然です。
それでも今回、
«「EC2からRDSへ通信するための情報をOutputしたい」»
という発想が出てきました。
これはかなり重要です。
単に、
«「Outputは値を表示するもの」»
と暗記するのではなく、
誰が?
↓
何のために?
↓
どの情報が必要?
↓
その情報をOutputする
という考え方ができています。
🎓 次回予告
次回はいよいよ、
Module
に入ります。
ここからTerraformの「部品化」が始まります。
今までは例えば、
EC2
Security Group
Subnet
IAM
などを、それぞれTerraformに書いてきました。
でも、実務で同じような構成を何度も作るなら、
毎回全部書くのは大変です。
そこで、
EC2
+
Security Group
+
その他の設定
を一つの「部品」としてまとめます。
そして、
module "web" {
}
のように呼び出して使えるようにします。
イメージとしては、
今まで
EC2
SG
Subnet
IAM
などを
毎回書く
↓
Module
┌─────────────────┐
│ Web Server │
│ │
│ EC2 │
│ Security Group │
│ その他の設定 │
└─────────────────┘
↓
module "web"
です。
つまりModuleは、
«Terraformで作った構成を「部品」として再利用する仕組み»
です。
Variablesで、
«設定値を外から変えられるようにする»
Outputsで、
«作ったリソースの必要な情報を外へ出せるようにする»
そして次のModuleで、
«複数のTerraformコードを一つの部品として再利用する»
という流れになります。
ここまで来ると、Terraformがかなり「設計ツール」らしく見えてきます。
そしてModuleを理解したら、いよいよ次の段階。
「要件だけ渡すから、ゼロからAWS構成を設計してTerraformを書いてみる」
という実務総合演習に進めます。
ここからは、今まで覚えた知識がバラバラではなく、
VPC
↓
Subnet
↓
Route
↓
ALB
↓
EC2
↓
RDS
↓
Security Group
↓
Variables
↓
Outputs
↓
Module
という一本の設計としてつながっていきます。🔥☁️🚀