はじめに
はじめまして。Webアプリケーションのエンジニアをやっているえびです。
Terraformを使って見たかったのですが、なかなか機会がなくチャレンジできていませんでしたが、今度実務で使いそうなのでキャッチアップを兼ねてチュートリアルと見つけたコマンドをまとめようと思いました。
後発者の方の参考になりますと幸いです。
Terraform とは
Terraformは、HashiCorp社が開発したインフラストラクチャを安全かつ効率的に構築、変更、バージョン管理できるコードツールです。コンピューティングインスタンス、ストレージ、ネットワークといった低レベルコンポーネントから、DNSエントリやSaaS機能といった高レベルコンポーネントまで、あらゆるインフラストラクチャの構築、変更、バージョン管理をサポートします。
部分引用: https://developer.hashicorp.com/terraform
つまりIaCの一種です。
IaCとは
インフラ構築のコード化により、自動で環境構築をしてくれるため、手作業による人為的なミスを削減することが可能です。
引用: https://www.ntt-tx.co.jp/column/iac/230815/
チュートリアルをやってみる。
前提
- WSL + DevContainer(debian-bookworm)環境にてコマンドを実行しています。
- 公式のチュートリアルに沿って実施しています。
https://developer.hashicorp.com/terraform/tutorials
AWSコマンドとterraformコマンドインストール
Dockerfileに以下を追加してリビルド
# AWS CLI
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
&& unzip awscliv2.zip \
&& ./aws/install \
&& rm -rf awscliv2.zip aws
# Terraform
RUN curl -fsSL https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com bookworm main" > /etc/apt/sources.list.d/hashicorp.list \
&& apt-get update \
&& apt-get install -y terraform \
&& rm -rf /var/lib/apt/lists/*
簡単なEC2単体構成を作成
terraform.tfを作成
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.92"
}
}
required_version = ">= 1.2"
}
main.tfを作成
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
tags = {
Name = "learn-terraform"
}
}
ファイルをフォーマット
terraform fmt
Terraformワークスペースを初期化
main.tfと同じディレクトリで以下のコマンドを実行
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.92"...
- Installing hashicorp/aws v5.98.0...
- Installed hashicorp/aws v5.98.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
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.
内容を検証する
$ terraform validate
Success! The configuration is valid.
リソースを作成する
node@7621048e52f3:~/front/deploy (feature/#11) $ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0fe9a26b158ea885a]
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.app_server will be created
+ resource "aws_instance" "app_server" {
+ ami = "ami-0fe9a26b158ea885a"
+ arn = (known after apply)
+ associate_public_ip_address = (known after apply)
+ availability_zone = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ enable_primary_ipv6 = (known after apply)
+ force_destroy = false
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = (known after apply)
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_group_id = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ region = "us-west-2"
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Name" = "learn-terraform"
}
+ tags_all = {
+ "Name" = "learn-terraform"
}
+ tenancy = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
+ capacity_reservation_specification (known after apply)
+ cpu_options (known after apply)
+ ebs_block_device (known after apply)
+ enclave_options (known after apply)
+ ephemeral_block_device (known after apply)
+ instance_market_options (known after apply)
+ maintenance_options (known after apply)
+ metadata_options (known after apply)
+ network_interface (known after apply)
+ primary_network_interface (known after apply)
+ private_dns_name_options (known after apply)
+ root_block_device (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes ←yesと入力する
aws_instance.app_server: Creating...
ワークスペースのリソースのリストを確認
$ terraform state list
data.aws_ami.ubuntu
aws_instance.app_server
変数と出力
variables.tfを作成
variable "instance_name" {
description = "Value of the EC2 instance's Name tag."
type = string
default = "learn-terraform"
}
variable "instance_type" {
description = "The EC2 instance's type."
type = string
default = "t2.micro"
}
main.tfの以下の行を追加する
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
- instance_type = "t2.micro"
+ instance_type = var.instance_type
tags = {
Name = "learn-terraform"
Name = var.instance_name
}
}
outputs.tfを作成
output "instance_hostname" {
description = "Private DNS name of the EC2 instance."
value = aws_instance.app_server.private_dns
}
変更を反映
$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0026a04369a3093cc]
aws_instance.app_server: Refreshing state... [id=i-0c636e158c30e48f9]
Changes to Outputs:
+ instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"
You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"
変更を確認
$ terraform output
instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal"
モジュールブロック
main.tfを修正
下記を追記
+ module "vpc" {
+ source = "terraform-aws-modules/vpc/aws"
+ version = "5.19.0"
+
+ name = "example-vpc"
+ cidr = "10.0.0.0/16"
+
+ azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
+ private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
+ public_subnets = ["10.0.101.0/24"]
+
+ enable_dns_hostnames = true
+ }
下記2行を追加
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
+ vpc_security_group_ids = [module.vpc.default_security_group_id]
+ subnet_id = module.vpc.private_subnets[0]
tags = {
Name = var.instance_name
}
}
VPCモジュールをインストール
$ terraform init
Initializing the backend...
Initializing modules...
Downloading registry.terraform.io/terraform-aws-modules/vpc/aws 5.19.0 for vpc...
- vpc in .terraform/modules/vpc
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Using previously-installed hashicorp/aws v5.98.0
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.
変更を計画して適用
$ terraform apply
data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0026a04369a3093cc]
aws_instance.app_server: Refreshing state... [id=i-0c636e158c30e48f9]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_instance.app_server must be replaced
-/+ resource "aws_instance" "app_server" {
~ arn = "arn:aws:ec2:us-west-2:949008909725:instance/i-0c636e158c30e48f9" -> (known after apply)
~ associate_public_ip_address = true -> (known after apply)
~ availability_zone = "us-west-2b" -> (known after apply)
##...
}
}
Plan: 16 to add, 0 to change, 1 to destroy.
Changes to Outputs:
~ instance_hostname = "ip-172-31-35-26.us-west-2.compute.internal" -> (known after apply)
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.app_server: Destroying... [id=i-0fbb487cbdf2eb6ed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m10s elapsed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m20s elapsed]
aws_instance.app_server: Still destroying... [id=i-0fbb487cbdf2eb6ed, 00m30s elapsed]
aws_instance.app_server: Destruction complete after 31s
module.vpc.aws_vpc.this[0]: Creating...
module.vpc.aws_vpc.this[0]: Still creating... [00m10s elapsed]
module.vpc.aws_vpc.this[0]: Creation complete after 12s [id=vpc-01e157ec1af2d7314]
module.vpc.aws_subnet.private[0]: Creating...
module.vpc.aws_route_table.private[1]: Creating...
module.vpc.aws_subnet.private[1]: Creating...
module.vpc.aws_route_table.private[0]: Creating...
module.vpc.aws_default_route_table.default[0]: Creating...
module.vpc.aws_subnet.public[0]: Creating...
module.vpc.aws_internet_gateway.this[0]: Creating...
module.vpc.aws_default_security_group.this[0]: Creating...
## ...
module.vpc.aws_route_table_association.private[1]: Creation complete after 0s [id=rtbassoc-0ea0b41646a73659c]
module.vpc.aws_route_table_association.private[0]: Creation complete after 0s [id=rtbassoc-0d51502051aa2c2af]
module.vpc.aws_default_network_acl.this[0]: Creation complete after 2s [id=acl-03b6b08c6f6a81e4a]
module.vpc.aws_route.public_internet_gateway[0]: Creation complete after 1s [id=r-rtb-0cde73c077eadf7e61080289494]
module.vpc.aws_default_security_group.this[0]: Creation complete after 3s [id=sg-04f350f66843618db]
module.vpc.aws_subnet.public[0]: Creation complete after 3s [id=subnet-02a7d5d6b6e8742d6]
module.vpc.aws_route_table_association.public[0]: Creating...
module.vpc.aws_route_table_association.public[0]: Creation complete after 1s [id=rtbassoc-03db7fad8ba24eca0]
aws_instance.app_server: Still creating... [00m10s elapsed]
aws_instance.app_server: Creation complete after 13s [id=i-0226232d8b6e9eea6]
Apply complete! Resources: 16 added, 0 changed, 1 destroyed.
Outputs:
instance_hostname = "ip-10-0-1-75.us-west-2.compute.internal"
リソース削除
main.tfを修正
main.tfのresourceをコメントアウトで囲います
+ /*
resource "aws_instance" "app_server" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
vpc_security_group_ids = [module.vpc.default_security_group_id]
subnet_id = module.vpc.private_subnets[0]
tags = {
Name = var.instance_name
}
}
+ */
outputs.tfを修正
+ /*
output "instance_hostname" {
description = "Private DNS name of the EC2 instance."
value = aws_instance.app_server.private_dns
}
+ */
削除を適用
$ terraform apply
data.aws_ami.ubuntu: Reading...
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-01e157ec1af2d7314]
aws_instance.app_server: Refreshing state... [id=i-0226232d8b6e9eea6]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0a605bc2ef5707a18]
module.vpc.aws_default_route_table.default[0]: Refreshing state... [id=rtb-0c1f047c07a84c278]
module.vpc.aws_default_security_group.this[0]: Refreshing state... [id=sg-04f350f66843618db]
## ...
Plan: 0 to add, 0 to change, 1 to destroy.
Changes to Outputs:
- instance_hostname = "ip-10-0-1-75.us-west-2.compute.internal" -> null
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_instance.app_server: Destroying... [id=i-0226232d8b6e9eea6]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m10s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m20s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m30s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m40s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 00m50s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 01m00s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 01m10s elapsed]
aws_instance.app_server: Still destroying... [id=i-0226232d8b6e9eea6, 01m20s elapsed]
aws_instance.app_server: Destruction complete after 1m22s
Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
ワークスペースを削除
$ terraform destroy
data.aws_ami.ubuntu: Reading...
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-01e157ec1af2d7314]
data.aws_ami.ubuntu: Read complete after 1s [id=ami-0a605bc2ef5707a18]
module.vpc.aws_default_security_group.this[0]: Refreshing state... [id=sg-04f350f66843618db]
module.vpc.aws_default_route_table.default[0]: Refreshing state... [id=rtb-0c1f047c07a84c278]
module.vpc.aws_subnet.private[1]: Refreshing state... [id=subnet-0d2376b2fad1af4a6]
module.vpc.aws_subnet.private[0]: Refreshing state... [id=subnet-0d2219235033fe9d0]
## ...
Plan: 0 to add, 0 to change, 15 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
module.vpc.aws_route_table_association.private[1]: Destroying... [id=rtbassoc-0ea0b41646a73659c]
module.vpc.aws_default_security_group.this[0]: Destroying... [id=sg-04f350f66843618db]
module.vpc.aws_default_route_table.default[0]: Destroying... [id=rtb-0c1f047c07a84c278]
module.vpc.aws_route.public_internet_gateway[0]: Destroying... [id=r-rtb-0cde73c077eadf7e61080289494]
## ...
module.vpc.aws_subnet.private[1]: Destruction complete after 1s
module.vpc.aws_subnet.public[0]: Destruction complete after 1s
module.vpc.aws_subnet.private[0]: Destruction complete after 1s
module.vpc.aws_internet_gateway.this[0]: Destruction complete after 0s
module.vpc.aws_route_table.private[1]: Destruction complete after 1s
module.vpc.aws_route_table.private[0]: Destruction complete after 1s
module.vpc.aws_route_table.public[0]: Destruction complete after 1s
module.vpc.aws_vpc.this[0]: Destroying... [id=vpc-01e157ec1af2d7314]
module.vpc.aws_vpc.this[0]: Destruction complete after 1s
Destroy complete! Resources: 15 destroyed.
コマンドまとめ
| コマンド | 説明 |
|---|---|
| terraform fmt | .tfファイルをフォーマットする |
| terraform init | .tfファイルに記述されているモジュールなどをインストール |
| terraform validate | .tfファイルの内容を検証する |
| terraform apply | デプロイを行う |
| terraform output | outputブロックで定義した出力を確認 |
| terraform state list | ワークスペースのリソースのリストを確認 |
| terraform show | ワークスペース全体の状態を出力 |
| terraform destroy | ワークスペースの破棄 |