Terraformの基礎2(Terraform学習備忘録 #2)
Terraformを学習内容を備忘録として残します。versionはv1.6です。
変数
アクセスキーやシークレットキーなどの機密情報を設定する場合、Gitのリポジトリにそれらを含めるべきではありません。Gitリポジトリにupしない別ファイルを作成して、そこに機密情報を記述し、変数としてそれらを別ファイルで使用することができるようにします。
## instance.tf
resource "aws_instance" "example" {
ami = lookup(var.AMIS, var.AWS_REGION, "") # last parameter is the default value
instance_type = "t2.micro"
}
lookup
は、マップのキーに対応する要素の値を取得します。指定されたキーが存在しない場合は、代わりに指定されたデフォルト値が返されます。
## provider.tf
provider "aws" {
access_key = var.AWS_ACCESS_KEY
secret_key = var.AWS_SECRET_KEY
region = var.AWS_REGION
}
## vars.tf
variable "AWS_ACCESS_KEY" {
}
variable "AWS_SECRET_KEY" {
}
variable "AWS_REGION" {
default = "eu-west-1"
}
variable "AMIS" {
type = map(string)
default = {
us-east-1 = "ami-13be557e"
us-west-2 = "ami-06b94666"
eu-west-1 = "ami-0d729a60"
}
}
## terraform.tfvars
AWS_ACCESS_KEY = "AWSのアクセスキーを記述"
AWS_SECRET_KEY = "AWSのシークレットアクセスキーを記述"
terraform.tfvars
で宣言した変数は自動的に読み込まれます。
ファイルのアップロード
AWSを起動した後に、何かソフトウェアをインストールしたり、ファイルをアップロードしたりする場面が出てきます。ファイルプロビジョナはTerraformを実行しているマシンから新しく作成されたリソースにファイルやディレクトリをコピーします。ファイルプロビジョナは ssh と winrm の両方の接続に対応しています。ただし、プロビジョナは最後の手段であり、別の方法でできなかった場合に使用することが推奨されます。
## instance.tf
resource "aws_key_pair" "mykey" {
key_name = "mykey"
public_key = file(var.PATH_TO_PUBLIC_KEY)
}
resource "aws_instance" "example" {
ami = var.AMIS[var.AWS_REGION]
instance_type = "t2.micro"
key_name = aws_key_pair.mykey.key_name
provisioner "file" {
source = "script.sh"
destination = "/tmp/script.sh"
}
connection {
host = coalesce(self.public_ip, self.private_ip)
type = "ssh"
user = var.INSTANCE_USERNAME
private_key = file(var.PATH_TO_PRIVATE_KEY)
}
}
provisioner "file"
の部分でアップロードするファイルを指定しています。ファイルアップロードの際には、SSHまたはWinRM経由でリモートリソースにアクセスする必要があります。その設定を connection
で行っています。
file
は、与えられたパスにあるファイルの内容を読み込み、文字列として返します。
## vars.tf
variable "AWS_ACCESS_KEY" {
}
variable "AWS_SECRET_KEY" {
}
variable "AWS_REGION" {
default = "eu-west-1"
}
variable "AMIS" {
type = map(string)
default = {
us-east-1 = "ami-13be557e"
us-west-2 = "ami-06b94666"
eu-west-1 = "ami-844e0bf7"
}
}
variable "PATH_TO_PRIVATE_KEY" {
default = "mykey"
}
variable "PATH_TO_PUBLIC_KEY" {
default = "mykey.pub"
}
variable "INSTANCE_USERNAME" {
default = "ubuntu"
}
スクリプトの実行
スクリプトを実行する場合も、プロビジョナを使用します。remote-exec
プロビジョナは、リモートリソースが作成された後、リモートリソース上でスクリプトを呼び出します。これは、設定管理ツールの実行、クラスタへのブートストラップなどに使用できます。ローカルプロセスを呼び出すには、代わりに local-exec
プロビジョナを参照してください。remote-exec
プロビジョナは接続を必要とし、ssh
と winrm
の両方をサポートします。
## instance.tf
resource "aws_key_pair" "mykey" {
key_name = "mykey"
public_key = file(var.PATH_TO_PUBLIC_KEY)
}
resource "aws_instance" "example" {
ami = var.AMIS[var.AWS_REGION]
instance_type = "t2.micro"
key_name = aws_key_pair.mykey.key_name
provisioner "file" {
source = "script.sh"
destination = "/tmp/script.sh"
}
provisioner "remote-exec" {
inline = [
"chmod +x /tmp/script.sh",
"sudo sed -i -e 's/\r$//' /tmp/script.sh", # Remove the spurious CR characters.
"sudo /tmp/script.sh",
]
}
connection {
host = coalesce(self.public_ip, self.private_ip)
type = "ssh"
user = var.INSTANCE_USERNAME
private_key = file(var.PATH_TO_PRIVATE_KEY)
}
}
設定情報の出力, ローカルホスト上への出力
出力値は、インフラに関する情報をコマンドラインで利用可能にし、他のTerraformの設定で利用できるように情報を公開することができます。出力値はプログラミング言語の戻り値に似ています。
## instance.tf
resource "aws_instance" "example" {
ami = var.AMIS[var.AWS_REGION]
instance_type = "t2.micro"
provisioner "local-exec" {
command = "echo ${aws_instance.example.private_ip} >> private_ips.txt"
}
}
output "ip" {
value = aws_instance.example.public_ip
}
上記の例では、立ち上げたAWSインスタンスのパブリックIPアドレスを ip
という名前で出力しており、他のファイルでも使えるようにしています。またプライベートIPアドレスをローカルの priavte_ips.txt
ファイルに出力しています。
local-exec provisioner
はリソースの作成後にローカル実行ファイルを呼び出します。これはリソース上ではなく、Terraformを実行しているマシン上のプロセスを呼び出します。
$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
+ create
Terraform will perform the following actions:
# aws_instance.example will be created
+ resource "aws_instance" "example" {
+ ami = "ami-0c4f29be32be77ca3"
(省略)
aws_instance.example: Still creating... [20s elapsed]
aws_instance.example: Provisioning with 'local-exec'...
aws_instance.example (local-exec): Executing: ["/bin/sh" "-c" "echo 172.31.6.134 >> private_ips.txt"]
aws_instance.example: Creation complete after 23s [id=i-06fc3811d420fdc2a]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
ip = "18.183.56.31"
State
Terraformの実行結果は、stateファイルを使って管理されます。terraform apply
を実行すると、必ずstateファイルが作成されます。このstateファイルには、Terraformの実行によってどのようなリソースが作成されたか、そのリソースのIDや設定内容などが保存されています。これは、Terraformがどのリソースを作成、更新、または削除する必要があるかを判断するために使用されます。
具体的には、以下のような目的でstateが必要となります。
- リソースの追跡:Terraformはstateを使用して、作成したリソースを追跡します。これにより、次回の
terraform apply
時に既存のリソースを認識し、それらを更新または削除することができます。 - 差分の計算:Terraformはstateと現在の設定(.tfファイル)を比較することで、どのリソースが作成、更新、または削除されるべきかを判断します。これにより、必要な変更だけを適用することができます。
- 共有と協調:リモートの
backend
を使用すると、複数の人間やシステムが同じTerraform設定を共有し、協調して作業することが可能になります。これにより、同時開発による状態の破損を防ぐことができます。
Stateファイルの作成手順は下記です。
- S3バケットとDynamoDBテーブルを作成します。S3バケットはstateファイルの保存場所として、DynamoDBテーブルはstateのロック管理として使用します。
- Terraformの設定ファイル(.tfファイル)にて、
backend
として上記のS3バケットとDynamoDBテーブルを指定します。
Backend
Terraformの backend
は、stateファイルの保存場所とロックの管理を担当します。S3やDynamoDBなどをbackend
として利用することができます。リモートに状態を保存し、編集ロックすることで、同時開発による状態の破損を防ぐことができます。また、秘密情報を含むtfstateをGitHub等に上げることを回避できます。下記の例では backend
でs3を指定しています。
## backend.tf
terraform {
backend "s3" {
bucket = "terraform-tsstate-20231106"
key = "terraform/demo4"
region = "ap-northeast-1"
}
}
Data Sources
Terraformの Data Sources
は、既存のインフラリソースのデータを参照・読み取るための方法です。Data Sources
を使用すると、Terraformの外部で定義された情報、または別のTerraform構成によって定義された情報を参照することが可能となります。
下記の例では、AWSの全IPリスト(Data Sources)の中から東京と大阪のリージョンのipアドレス取得し、それらを全て許可するセキュリティグループを新しく作成します。
## securitygroup.tf
data "aws_ip_ranges" "asian_ec2" {
regions = ["ap-northeast-1", "ap-northeast-3"]
services = ["ec2"]
}
resource "aws_security_group" "from_asia" {
name = "from_asia"
ingress {
from_port = "443"
to_port = "443"
protocol = "tcp"
cidr_blocks = slice(data.aws_ip_ranges.asian_ec2.cidr_blocks, 0, 50)
}
tags = {
CreateDate = data.aws_ip_ranges.asian_ec2.create_date
SyncToken = data.aws_ip_ranges.asian_ec2.sync_token
}
}
Terraformの Data Sources
を使用することには、以下のようなメリットがあります。
- 既存リソースの参照:
Data Sources
を使用すると、Terraformの外部で作成されたリソースや、別のTerraform設定で作成されたリソースの情報を参照することができます。これにより、既存のリソースを再利用したり、他のリソースの設定に基づいて新しいリソースを作成したりすることが可能となります。 - 動的な設定:
Data Sources
を使用すると、リソースの設定を動的に決定することができます。例えば、特定の条件を満たす最新のAMIを検索して、それを使用してEC2インスタンスを作成する、といったことが可能です。 - コードの再利用:
Data Sources
を使用すると、一部のリソース設定を他のTerraform設定で再利用することができます。これにより、コードの重複を避け、管理を効率化することができます。
templatefile関数
templatefile
は与えられたパスにあるファイルを読み込み、与えられたテンプレート変数のセットを使ってその内容をテンプレートとしてレンダリングします。以前は、template_file
データソースを使用することが一般的でしたが、v0.12以降は非推奨となっており、templatefile
関数を使用することが推奨されています。templatefile
関数は、ファイルからテンプレートを読み込み、指定した変数でテンプレートをレンダリングします。これにより、外部ファイルからテンプレートを読み込む際に、よりシンプルで読みやすいコードを書くことができます。
Terraform言語における文字列はUnicode文字のシーケンスなので、この関数はファイルの内容をUTF-8エンコードされたテキストとして解釈し、その結果のUnicode文字を返します。もしファイルに無効な UTF-8 シーケンスが含まれていれば、この関数はエラーを返します。
この関数は Terraform の実行開始時に既にディスク上に存在するファイルに対してのみ使用できます。関数は依存関係グラフに参加しないので、この関数は Terraform 実行中に動的に生成されるファイルには使用できません。
テンプレートファイルには *.tftpl
という命名パターンが推奨されます。Terraformは他の名前を使うことを妨げませんが、この規則に従うことでエディタが内容を理解しやすくなり、結果としてより良い編集環境を提供できる可能性が高くなります。
locals {
template = templatefile("${path.module}/template.tpl", { var1 = "value1", var2 = "value2" })
}
templatefile
関数の第1引数には対象ファイルのパス、第2引数には変数を指定します。
Module
Terraformの module
は、複数のインフラリソースをまとめてテンプレート化する機能です。これにより、同じリソースを複数作成したり、ほとんど同じだけど少し設定が異なるリソースを一つのソースコードとして管理することができます。
具体的には、以下のような特徴があります。
- 再利用可能:
module
は一度定義すれば何度でも再利用することができます。これにより、同じ設定のリソースを何度も作成する必要がなくなります。 - パラメータ化:
module
はパラメータを受け取ることができます。これにより、同じmodule
を使いつつ、パラメータによって挙動を変えることができます。 - 抽象化:
module
は複数のリソースを一つの単位として扱うことができます。これにより、複雑なリソースの構成をシンプルに扱うことができます。
## modules.tf
module "consul" {
source = "github.com/wardviaene/terraform-consul-module.git?ref=terraform-0.12"
key_name = aws_key_pair.mykey.key_name
key_path = var.PATH_TO_PRIVATE_KEY
region = var.AWS_REGION
vpc_id = aws_default_vpc.default.id
subnets = {
"0" = aws_default_subnet.default_az1.id
"1" = aws_default_subnet.default_az2.id
"2" = aws_default_subnet.default_az3.id
}
}
output "consul-output" {
value = module.consul.server_address
}
## default_vpc.tf
# default VPC
resource "aws_default_vpc" "default" {
tags = {
Name = "Default VPC"
}
}
# default subnets
resource "aws_default_subnet" "default_az1" {
availability_zone = "${var.AWS_REGION}a"
tags = {
Name = "Default subnet for ${var.AWS_REGION}a"
}
}
resource "aws_default_subnet" "default_az2" {
availability_zone = "${var.AWS_REGION}c"
tags = {
Name = "Default subnet for ${var.AWS_REGION}b"
}
}
resource "aws_default_subnet" "default_az3" {
availability_zone = "${var.AWS_REGION}d"
tags = {
Name = "Default subnet for ${var.AWS_REGION}c"
}
}
上記の例ではgithubからモジュールをダウンロードします。モジュールをダウンロードするには terraform get
コマンドを使用します。
$ terraform get
Downloading git::https://github.com/wardviaene/terraform-consul-module.git?ref=terraform-0.12 for consul...
- consul in .terraform/modules/consul
コマンド概要
terraform fmt
Terraformの設定ファイルを標準的なフォーマットとスタイルに書き換えるために使います。このコマンドは、Terraformの言語スタイルの規約のサブセットを適用し、可読性のために他の細かい調整も行います。
terraform graph
設定や実行計画を視覚的に表現するために使用します。出力はDOT形式で、GraphVizでグラフを生成することができます。
terraform import [options] ADDRESS ID
IDから既存のリソースを見つけ、指定されたADDRESSにあるTerraformの状態にインポートします。IDはterraformの公式ドキュメントに記載があります。
terraform output
ルートモジュールからの出力値を表示します。追加の引数を指定しないと、ルート・モジュールのすべての出力を表示しまsu。出力NAMEが指定されると、その出力の値のみが表示されます。
terraform apply -refresh-only
検出した変更を状態にコミットする前に確認する機会を得ることができます。検出された変更を確認するための対話型プロンプトが表示されます。
terraform show
ステートファイルやプランファイルから人間が読める出力を提供するために使われます。これは、計画されたオペレーションが期待通りであることを確認するためにプランを検査したり、Terraformが見ている現在の状態を検査するために使うことができます。機械可読な出力は -json コマンドラインフラグを追加することで生成されます。
terraform state
高度な状態管理に使用します。状態を直接変更するのではなく、terraform state
コマンドを利用することで多くのケースに対応できます。
terraform validate
ディレクトリ内の設定ファイルを検証します。設定のみを参照し、リモートステートやプロバイダAPIなどのリモートサービスにはアクセスしません。提供された変数や既存の状態に関係なく、設定が構文的に有効かどうか、内部的に一貫しているかどうかを検証するチェックを実行します。そのため、属性名や値の型の正しさなど、再利用可能なモジュールの一般的な検証に主に役立ちます。
terraform apply -replace=ADDRESS
リソースインスタンスを指定されたアドレスに置き換えるようTerraformに指示します。これは1つ以上のリモートオブジェクトがデグレしてしまった場合に有用で、同じ構成の置換オブジェクトを使用して不変のインフラパターンに合わせることができます。Terraformは、指定されたリソースが通常 “update “アクションを引き起こすか、まったくアクションを引き起こさない場合、”replace “アクションを使用します。複数のオブジェクトを一度に置き換えるには、このオプションを複数回指定します。
terraform untaint
Terraformには “tainted “というマーカーがあり、オブジェクトが損傷している可能性があることを追跡するのに使います。Terraformは複数ステップの “create “アクション中にエラーが発生した場合、自動的にオブジェクトを “tainted “としてマークします。
非推奨コマンドである terraform taint
を使って手動でオブジェクトを “tainted “にすることもできますが、このワークフローはもはや推奨されていません。
Terraform Cloud
2023/11時点では、リモートバックエンドではなく、Terraform Cloudを使うことが推奨されています。
Terraform CloudはTerraformをチームで利用するためのアプリケーションです。Terraformの実行を一貫性のある信頼性の高い環境で管理し、共有された状態やシークレットデータへの容易なアクセス、インフラストラクチャへの変更を承認するためのアクセスコントロール、Terraformモジュールを共有するためのプライベートレジストリ、Terraformの設定内容を管理するための詳細なポリシーコントロールなどが含まれています。
Terraform Cloudはホスティングサービスとしてhttps://app.terraform.io。小規模なチームは無料でサインアップし、Terraformをバージョン管理に接続し、変数を共有し、安定したリモート環境でTerraformを実行し、リモートの状態を安全に保存することができます。有料版では、5人以上のユーザーを追加したり、異なる権限レベルのチームを作成したり、より効果的なコラボレーションを行うことができます。
Terraform CLIでTerraform Cloudを使う
Terraform CLIとTerraform Cloudの連携により、Terraform CloudとTerraform Enterpriseをコマンドラインで利用することができます。ドキュメント内の Terraform Cloud の説明は、明示されている場合を除き、Terraform Enterprise にも適用されます。
コマンドラインからTerraform Cloudを利用することをCLI駆動の実行ワークフローと呼びます。CLIワークフローを使用すると、terraform plan
や terraform apply
のような操作はデフォルトでTerraform Cloudの実行環境でリモート実行され、ログ出力はローカルターミナルにストリーミングされます。これにより、Terraform Cloudワークスペースで暗号化された変数、コスト見積もり、ポリシーチェックなど、使い慣れたTerraform CLIワークフローの中でTerraform Cloudの機能を使うことができます。