【AWS】TerraformをAmazonLinux2023にインストールする

本サイトで紹介している商品・サービス等の外部リンクには、プロモーションが含まれています。

今回はAmazonLinux2023にTerraformをインストールして、実際にリソースを作成する方法を解説します。

目次

システム構成

今回使用したソフトウェアとバージョンは以下のとおりです。

  • OS: Amazon Linux 2023.5.20241001
  • Terraform: 1.9.8

コマンドはすべてrootユーザーで実行しています。

Terraformのインストール

リポジトリを追加する

AmazonLinux2023の標準リポジトリにはTerraformが入っていないので、HashiCorpのリポジトリを追加します。

# dnf config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo

Terraformをインストールする

# dnf -y install terraform
# terraform -v
Terraform v1.9.8
on linux_amd64

これでTerraformのインストールは完了です。簡単ですね。

Terraform実行事前準備

Terraform用のIAMロールを作成する

TerraformでAWSリソースを作成するためには、Terraformにクレデンシャル情報を渡してあげる必要があります。

EC2からTerraformを実行する場合は、EC2にアタッチされたIAMロールの認証情報を利用してAWSリソースを操作することができます。

事前にTerraform用のIAMロールを作成し、EC2インスタンスにアタッチしておきましょう。

『マネジメントコンソール』→『IAM』→『ロール』→『ロールを作成』を作成をクリックします。

ステップ1: 信頼されたエンティティを選択で『AWSのサービス』にチェックを入れ、『ユースケース』から『EC2』を選択し、『次へ』をクリックします。

ステップ2: 許可を追加で、アタッチするIAMポリシーを選択します。

今回は検証なので『AdministratorAccess』をアタッチします。業務で使用する場合は適切な権限を持つポリシーをアタッチしてください。

ステップ3: 名前、確認、および作成で『ロール名』に任意のロール名を入力し、設定に間違いがないことを確認した上で『ロールを作成』をクリックします。

IAMロールをEC2インスタンスにアタッチする

作成したIAMロールを、TerraformをインストールしたEC2インスタンスにアタッチします。

『マネジメントコンソール』→『EC2』→『インスタンス』→『Terraformをインストールしたインスタンス』にチェックを入れ、『アクション』→『セキュリティ』→『IAMロールを変更』をクリックします。

先ほど作成したIAMロールを選択して、『IAMロールの更新』をクリックします。

tfファイル準備

AWS Providerを設定する

事前準備が終わったら、EC2インスタンス上でtfファイルを作成します。

まずはTerraformでAWSリソースを操作するためのプラグインを用意します。Terraform用のディレクトリと、Provider設定用のファイルを作成します。(ファイル名は*.tfの形式なら何でもいいです)

# mkdir terraform
# cd terraform
# touch provider.tf

provider.tfをviなどで開き、下記のように編集して保存します。

terraform {
// awsプロバイダーのバージョンを指定
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

// awsプロバイダーの設定
provider "aws" {
// regionを指定
  region = "us-east-1"
}

プロジェクトを初期化する(terraform init)

tfファイルがあるディレクトリでterraform initを実行します。

「Terraform has been successfully initialized!」と表示されれば初期化完了です。カレントディレクトリに「.terraform」ディレクトリと「.terraform.lock.hcl」が生成されます。

# terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.72.1...
- Installed hashicorp/aws v5.72.1 (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.

# ls -la
total 8
drwxr-xr-x. 3 root root   70 Oct 21 14:22 .
dr-xr-x---. 5 root root  172 Oct 21 14:27 ..
drwxr-xr-x. 3 root root   23 Oct 21 14:22 .terraform
-rw-r--r--. 1 root root 1406 Oct 21 14:22 .terraform.lock.hcl
-rw-r--r--. 1 root root  264 Oct 21 14:08 provider.tf

tfファイルを作成する

リソースを定義するtfファイルを作成します。今回はネットワーク関連のリソースを一式作成してみます。

variables.tf

まずは変数宣言用のファイルを作成します。

# touch variables.tf

variables.tfをviなどで開き、変数を宣言します。

variable "aws_region" {}
variable "vpc" {
  type = map(string)
}

dev.auto.tfvars

次に変数に値をセットするためのファイルを作成します。

# touch dev.auto.tfvars

*.tfvarsでは変数の宣言はできないため、variables.tfで変数を宣言します。
variables.tfでも値のセットは可能ですが、*.tfvarsを使用することにより、例えばprodやdevなどの環境ごとに異なる値でTerraformを実行することができます。

ちなみにtfvarsの前にautoをつけると、Terraform実行時に変数ファイルを自動で読み取ってくれます。

作成したファイルを下記のように編集します。(CIDRは好みで変えてOKです)

aws_region = "us-east-1"
vpc = {
  "cidr-vpc"       = "10.150.0.0/16"
  "cidr-public-a"  = "10.150.10.0/24"
  "cidr-private-a" = "10.150.100.0/24"
}

これで変数ファイルの準備は完了です。

network.tf

VPC関連リソースを作成するためのファイルを作成します。

# touch network.tf

network.tfに各リソースの定義を書いていきます。

AWSリソース定義の書き方はTerraformのドキュメントに記載があるので、こちらを参考にコーディングします。

# VPC
resource "aws_vpc" "terraform-vpc" {
  cidr_block           = var.vpc["cidr-vpc"]
  enable_dns_hostnames = true

  tags = {
    Name = "terraform-vpc"
  }
}

# Internet Gateway
resource "aws_internet_gateway" "terraform-igw" {
  vpc_id = aws_vpc.terraform-vpc.id

  tags = {
    Name = "terraform-igw"
  }
}

# Public Subnet
resource "aws_subnet" "terraform-sbn-pub-1a" {
  vpc_id            = aws_vpc.terraform-vpc.id
  availability_zone = "${var.aws_region}a"
  cidr_block        = var.vpc["cidr-public-a"]

  tags = {
    "Name" = "terraform-sbn-pub-1a"
  }
}

# Private Subnet
resource "aws_subnet" "terraform-sbn-pri-1a" {
  vpc_id            = aws_vpc.terraform-vpc.id
  availability_zone = "${var.aws_region}a"
  cidr_block        = var.vpc["cidr-private-a"]

  tags = {
    "Name" = "terraform-sbn-pri-1a"
  }
}

# Route Table for Public Subnet
resource "aws_route_table" "terraform-rtb-pub" {
  vpc_id = aws_vpc.terraform-vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.terraform-igw.id
  }

  tags = {
    "Name" = "terraform-rtb-pub"
  }
}

resource "aws_route_table_association" "terraform-rtb-pub-1a" {
  subnet_id      = aws_subnet.terraform-sbn-pub-1a.id
  route_table_id = aws_route_table.terraform-rtb-pub.id
}

# Route Table for Private Subnet
resource "aws_route_table" "terraform-rtb-pri" {
  vpc_id = aws_vpc.terraform-vpc.id

  tags = {
    "Name" = "terraform-rtb-pri"
  }
}

resource "aws_route_table_association" "terraform-rtb-pri-1a" {
  subnet_id      = aws_subnet.terraform-sbn-pri-1a.id
  route_table_id = aws_route_table.terraform-rtb-pri.id
}

terraform fmtを実行する

terraform fmtは*tfや*.tfvarsなどのファイルのフォーマットを自動で整えてくれるコマンドです。

コーディングが完了したら実行しておきましょう。

# terraform fmt
## 引数にファイルを指定すると個別に実行できる
# terraform fmt network.tf

リソース作成

terraform planを実行する

terraform planは、どのようなリソースが作成・変更・削除されるか確認できるコマンドです。

次に実行するterraform applyの前の確認のために使用します。

# terraform plan
terraform planの結果
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_internet_gateway.terraform-igw will be created
  + resource "aws_internet_gateway" "terraform-igw" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "Name" = "terraform-igw"
        }
      + tags_all = {
          + "Name" = "terraform-igw"
        }
      + vpc_id   = (known after apply)
    }

  # aws_route_table.terraform-rtb-pri will be created
  + resource "aws_route_table" "terraform-rtb-pri" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = (known after apply)
      + tags             = {
          + "Name" = "terraform-rtb-pri"
        }
      + tags_all         = {
          + "Name" = "terraform-rtb-pri"
        }
      + vpc_id           = (known after apply)
    }

  # aws_route_table.terraform-rtb-pub will be created
  + resource "aws_route_table" "terraform-rtb-pub" {
      + arn              = (known after apply)
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                 = "0.0.0.0/0"
              + gateway_id                 = (known after apply)
                # (11 unchanged attributes hidden)
            },
        ]
      + tags             = {
          + "Name" = "terraform-rtb-pub"
        }
      + tags_all         = {
          + "Name" = "terraform-rtb-pub"
        }
      + vpc_id           = (known after apply)
    }

  # aws_route_table_association.terraform-rtb-pri-1a will be created
  + resource "aws_route_table_association" "terraform-rtb-pri-1a" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_route_table_association.terraform-rtb-pub-1a will be created
  + resource "aws_route_table_association" "terraform-rtb-pub-1a" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_subnet.terraform-sbn-pri-1a will be created
  + resource "aws_subnet" "terraform-sbn-pri-1a" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "us-east-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.150.100.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "terraform-sbn-pri-1a"
        }
      + tags_all                                       = {
          + "Name" = "terraform-sbn-pri-1a"
        }
      + vpc_id                                         = (known after apply)
    }

  # aws_subnet.terraform-sbn-pub-1a will be created
  + resource "aws_subnet" "terraform-sbn-pub-1a" {
      + arn                                            = (known after apply)
      + assign_ipv6_address_on_creation                = false
      + availability_zone                              = "us-east-1a"
      + availability_zone_id                           = (known after apply)
      + cidr_block                                     = "10.150.10.0/24"
      + enable_dns64                                   = false
      + enable_resource_name_dns_a_record_on_launch    = false
      + enable_resource_name_dns_aaaa_record_on_launch = false
      + id                                             = (known after apply)
      + ipv6_cidr_block_association_id                 = (known after apply)
      + ipv6_native                                    = false
      + map_public_ip_on_launch                        = false
      + owner_id                                       = (known after apply)
      + private_dns_hostname_type_on_launch            = (known after apply)
      + tags                                           = {
          + "Name" = "terraform-sbn-pub-1a"
        }
      + tags_all                                       = {
          + "Name" = "terraform-sbn-pub-1a"
        }
      + vpc_id                                         = (known after apply)
    }

  # aws_vpc.terraform-vpc will be created
  + resource "aws_vpc" "terraform-vpc" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.150.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = true
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags                                 = {
          + "Name" = "terraform-vpc"
        }
      + tags_all                             = {
          + "Name" = "terraform-vpc"
        }
    }

Plan: 8 to add, 0 to change, 0 to destroy.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

terraform planで出力された内容に問題がなければ次の手順に進みます。間違いがあった場合は、tfファイルを修正し、再度terraform planで確認します。

terraform applyを実行する

terraform applyは、tfファイルで定義したリソースを作成・変更するコマンドです。

コマンドを実行すると、途中まではplanと同じくどのような変更点があるのか表示されます。

最後に本当にapplyを実行してもいいのか聞かれるので、yesと入力すればリソースの作成・変更が適用されます。

# terraform apply

(省略)

Plan: 8 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

aws_vpc.terraform-vpc: Creating...
aws_vpc.terraform-vpc: Still creating... [10s elapsed]
aws_vpc.terraform-vpc: Creation complete after 12s [id=vpc-123456789]
aws_subnet.terraform-sbn-pub-1a: Creating...
aws_subnet.terraform-sbn-pri-1a: Creating...
aws_internet_gateway.terraform-igw: Creating...
aws_route_table.terraform-rtb-pri: Creating...
aws_internet_gateway.terraform-igw: Creation complete after 0s [id=igw-123456789]
aws_route_table.terraform-rtb-pub: Creating...
aws_route_table.terraform-rtb-pri: Creation complete after 0s [id=rtb-123456789]
aws_subnet.terraform-sbn-pub-1a: Creation complete after 0s [id=subnet-123456789]
aws_subnet.terraform-sbn-pri-1a: Creation complete after 0s [id=subnet-123456789]
aws_route_table_association.terraform-rtb-pri-1a: Creating...
aws_route_table_association.terraform-rtb-pri-1a: Creation complete after 1s [id=rtbassoc-123456789]
aws_route_table.terraform-rtb-pub: Creation complete after 1s [id=rtb-123456789]
aws_route_table_association.terraform-rtb-pub-1a: Creating...
aws_route_table_association.terraform-rtb-pub-1a: Creation complete after 0s [id=rtbassoc-123456789]

Apply complete! Resources: 8 added, 0 changed, 0 destroyed.

※IDの値は変えています。

Apply complete!』と表示されれば、リソースが正常に作成・変更されています。

マネジメントコンソールを見ると、新しくVPCが作成されていることが確認できます。

terraform stateでリソースを確認する

terraform stateはTerraformで管理しているリソースの状態を確認することができるコマンドです。

terraform state list

terraform state listを実行すると、Terraformでどのリソースを管理しているか一覧表示することができます。

# terraform state list
aws_internet_gateway.terraform-igw
aws_route_table.terraform-rtb-pri
aws_route_table.terraform-rtb-pub
aws_route_table_association.terraform-rtb-pri-1a
aws_route_table_association.terraform-rtb-pub-1a
aws_subnet.terraform-sbn-pri-1a
aws_subnet.terraform-sbn-pub-1a
aws_vpc.terraform-vpc

terraform state show

terraform state showでリソースの詳細を確認することができます。

# terraform state show aws_internet_gateway.terraform-igw
# aws_internet_gateway.terraform-igw:
resource "aws_internet_gateway" "terraform-igw" {
    arn      = "arn:aws:ec2:us-east-1:123456789:internet-gateway/igw-123456789"
    id       = "igw-0fb819242d45f525d"
    owner_id = "123456789"
    tags     = {
        "Name" = "terraform-igw"
    }
    tags_all = {
        "Name" = "terraform-igw"
    }
    vpc_id   = "vpc-123456789"
}

リソース削除

terraform destroyを実行する

Terraformで作成したリソースを削除するにはterraform destroyを実行します。

実行すると、applyと同様に削除予定のリソースの一覧が表示されます。

内容に問題がなければyesと入力すると、リソースが削除されます。

# terraform destroy

(省略)

Plan: 0 to add, 0 to change, 8 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

aws_route_table_association.terraform-rtb-pri-1a: Destroying... [id=rtbassoc-123456789]
aws_route_table_association.terraform-rtb-pub-1a: Destroying... [id=rtbassoc-123456789]
aws_route_table_association.terraform-rtb-pub-1a: Destruction complete after 0s
aws_route_table_association.terraform-rtb-pri-1a: Destruction complete after 0s
aws_route_table.terraform-rtb-pub: Destroying... [id=rtb-123456789]
aws_subnet.terraform-sbn-pub-1a: Destroying... [id=subnet-123456789]
aws_route_table.terraform-rtb-pri: Destroying... [id=rtb-123456789]
aws_subnet.terraform-sbn-pri-1a: Destroying... [id=subnet-123456789]
aws_subnet.terraform-sbn-pri-1a: Destruction complete after 1s
aws_subnet.terraform-sbn-pub-1a: Destruction complete after 1s
aws_route_table.terraform-rtb-pub: Destruction complete after 1s
aws_internet_gateway.terraform-igw: Destroying... [id=igw-123456789]
aws_route_table.terraform-rtb-pri: Destruction complete after 1s
aws_internet_gateway.terraform-igw: Destruction complete after 0s
aws_vpc.terraform-vpc: Destroying... [id=vpc-123456789]
aws_vpc.terraform-vpc: Destruction complete after 1s

Destroy complete! Resources: 8 destroyed.

Destroy complete!』と表示されれば削除完了です。

terraform destroy -target= で個別に削除する

terraform destroyのみで実行すると、すべてのリソースが削除されます。

すべてのリソースを丸ごと消すことはまれで、個別にリソースを削除するケースがほとんどだと思います。

terraform destroy実行時に-target=リソース名で削除したいものを指定することができます。

## ルートテーブルの関連付けだけを削除する。
# terraform destroy -target=aws_route_table_association.terraform-rtb-pub-1a
aws_vpc.terraform-vpc: Refreshing state... [id=vpc-123456789]
aws_internet_gateway.terraform-igw: Refreshing state... [id=igw-123456789]
aws_subnet.terraform-sbn-pub-1a: Refreshing state... [id=subnet-123456789]
aws_route_table.terraform-rtb-pub: Refreshing state... [id=rtb-123456789]
aws_route_table_association.terraform-rtb-pub-1a: Refreshing state... [id=rtbassoc-123456789]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_route_table_association.terraform-rtb-pub-1a will be destroyed
  - resource "aws_route_table_association" "terraform-rtb-pub-1a" {
      - id             = "rtbassoc-123456789" -> null
      - route_table_id = "rtb-123456789" -> null
      - subnet_id      = "subnet-123456789" -> null
        # (1 unchanged attribute hidden)
    }

Plan: 0 to add, 0 to change, 1 to destroy.
╷
│ Warning: Resource targeting is in effect
│
│ You are creating a plan with the -target option, which means that the result of this plan may not represent all of the changes requested by the
│ current configuration.
│
│ The -target option is not for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when
│ Terraform specifically suggests to use it as part of an error message.
╵

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

aws_route_table_association.terraform-rtb-pub-1a: Destroying... [id=rtbassoc-123456789]
aws_route_table_association.terraform-rtb-pub-1a: Destruction complete after 0s
╷
│ Warning: Applied changes may be incomplete
│
│ The plan was created with the -target option in effect, so some changes requested in the configuration may have been ignored and the output values
│ may not be fully updated. Run the following command to verify that no other changes are pending:
│     terraform plan
│
│ Note that the -target option is not suitable for routine use, and is provided only for exceptional situations such as recovering from errors or
│ mistakes, or when Terraform specifically suggests to use it as part of an error message.
╵

Destroy complete! Resources: 1 destroyed.

おまけ

Terraformに関する書籍は意外と数が少ないのですが、2024年11月26日にインプレスさんからTerraformの入門書が発売されるようです。

書籍でTerraformを学びたい方はぜひ!

この記事が気に入ったら
フォローしてね!

コメント

コメントする

目次