既存のAWS環境を後からTerraformでコード化する

はじめに

こんにちは、岩城です。
既存のAWS環境をTerraformでコード化する機会がありました。その際、Terraform素人であったため、コード化する流れを掴むまでに少し時間が掛かりました。 同じような方に向けて、備忘録として残しておきます。

用語解説

Terraform

  • Infrastructure as Codeを実現するためのツール
    • コードからインフラの構築/変更/バージョン管理を行える
  • HashiCorp社が開発、オープンソース版とエンタープライズ版がある
  • AWS、Azure、GCPを始めとするメジャーなクラウドプロバイダをサポートしている
  • 各クラウドの管理ツールで使わずにTerraformで管理できる
    • AWS: Cloud Formation
    • Azure: Resource Manager
    • GCP: Deployment Manager
  • コミュニティが活発でリリースが早いAWSのサービスに追従している
    • 中にはサポートされていないサービスやサービス内の個別パラメータもある
  • バイナリを実行パスに配置しておけば動作するので導入が簡単

Terraformの構成ファイル

本エントリで登場するTerraformの構成ファイルを紹介します。実際にはもっとあります。

  • xxxxxxx.tfstate
    • Terraformで管理してるインフラの状態が記述されたファイル
  • xxxxxxx.tf(tfファイル)
    • Terraformのコードが記述された設定ファイル

Terraformのコマンド

本エントリで登場するTerraformのコマンドを紹介します。実際にはもっとあります。

  • terraform init
    • Terraformの設定ファイルを含む作業ディレクトリを初期化します
  • terraform plan
    • 実行計画を作成します。既にTerraformでコード化された環境ならば、実際のリソースやパラメータを変更することなく、パラメータレベルで変更点を確認できます
  • terraform state show <リソース.リソース名>
    • tfstate内の各リソースの状態を確認します

本エントリには登場しませんが、リソースを作成・削除するコマンドも紹介しておきます。

  • terraform apply
    • 設定ファイルに従ってリソースを作成します
  • terraform destroy
    • Terraformが管理するリソースを削除します

Terraformが初めてで興味がお有りの方はこちら

以下を参考にセットアップや基本的な操作方法を確認してみてください。

既存のAWS環境をTerraformでコード化する流れ

お待たせしました。本題です。コード化する流れは以下のような感じです。

  • 作業ディレクトリの作成
  • プロバイダを指定したtfファイルを作成する
  • terraform initする
  • リソースタイプと名前を定義したtfファイルを作成する
    • リソースタイプや個別パラメータについては、とても分かり易い公式ドキュメントを参照しながら定義します
  • terraform importする
  • terraform state showしてtfファイルにパラメータを定義する
  • terraform planして差分がなくなるまでtfファイルを修正する

上記フローを見ただけでは分かりませんよね。実際にやってみます。

やってみた

構成

今回は、コード化の流れを掴むことを目的にしているので、VPCとサブネットだけの構成です。

仮にEC2を追加したとしても、やることは変わらないと思います。
モジュール化するパターンもあるかと思いますが、私自身まだ勉強中なので今回は触れません。

初期設定

作業ディレクトリでTerraformを実行できるように準備します。

  • 作業ディレクトリを作成する
  • プロバイダにAWSを指定する設定ファイルを作成する
$ cat provider.tf
provider "aws" {
  region = "ap-northeast-1"
}
  • terraform initにより初期化する
$ terraform init

Initializing provider plugins...

~省略~

* provider.aws: version = "~> 1.60"

Terraform has been successfully initialized!

~省略~

VPC

はじめにVPCをコード化します。

  • VPCに対応したリソースタイプと名前(自由)を宣言したtfファイルを予め作成する
    • ここではnetwork.tfファイルを作成してVPCリソースを定義します
$ cat network.tf
resource "aws_vpc" "terraform-sample-vpc" {

}
  • tfファイルで宣言したリソースタイプと名前、そしてVPCを特定するIDを指定してterraform importする
    • VPCのIDを特定するには、マネジメントコンソールやCLIで確認します
$ terraform import aws_vpc.terraform-sample-vpc <VPC ID> 

  • terraform state showしてリソース情報を確認する
$ terraform state show aws_vpc.terraform-sample-vpc
id                               = vpc-xxxxxxxxxxxxxxxxx
arn                              = arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:vpc/vpc-xxxxxxxxxxxxxxxxx
assign_generated_ipv6_cidr_block = false
cidr_block                       = 10.10.0.0/16
default_network_acl_id           = acl-xxxxxxxxxxxxxxxxx
default_route_table_id           = rtb-xxxxxxxxxxxxxxxxx
default_security_group_id        = sg-xxxxxxxxxxxxxxxxx
dhcp_options_id                  = dopt-xxxxxxxx
enable_classiclink               = false
enable_classiclink_dns_support   = false
enable_dns_hostnames             = false
enable_dns_support               = true
instance_tenancy                 = default
ipv6_association_id              = 
ipv6_cidr_block                  = 
main_route_table_id              = rtb-xxxxxxxxxxxxxxxxx
owner_id                         = xxxxxxxxxxxx
tags.%                           = 1
tags.Name                        = terraform-sample-vpc
  • tfstateファイルを元にtfファイルのパラメータを記述する
    • 以下の例では、デフォルト値は記載せず定義必須なパラメータやデフォルト値から変更したパラメータを定義します
$ cat network.tf
resource "aws_vpc" "terraform-sample-vpc" {
    cidr_block                       = "10.10.0.0/16"
    enable_dns_hostnames             = true
    tags {
        Name = "terraform-sample-vpc"
    }
}
  • 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.

aws_vpc.terraform-sample-vpc: Refreshing state... (ID: vpc-xxxxxxxxxxxxxxxxx)

------------------------------------------------------------------------

No changes. Infrastructure is up-to-date.

~省略~

Subnet

次にSubnetをコード化します。VPCを作成した時と流れは一緒です。

  • tfファイルを予め作成する
    • VPCと同じtfファイルに追記していますが、あくまで一例です。別のファイルで管理しても問題ありません
$ cat network.tf
resource "aws_vpc" "terraform-sample-vpc" {
    cidr_block                       = "10.10.0.0/16"
    enable_dns_hostnames             = true
    tags {
        Name = "terraform-sample-vpc"
    }
}

resource "aws_subnet" "terraform-sample-subnet-1a" {

}
  • Subnet IDを指定してterraform importする
$ terraform import aws_subnet.terraform-sample-subnet-1a <Subnet ID> 
  • terraform state showでリソース情報を確認する
$ terraform state show aws_subnet.terraform-sample-subnet-1a
id                              = subnet-xxxxxxxxxxxxxxxxx
arn                             = arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:subnet/subnet-xxxxxxxxxxxxxxxxx
assign_ipv6_address_on_creation = false
availability_zone               = ap-northeast-1a
availability_zone_id            = apne1-az4
cidr_block                      = 10.10.11.0/24
ipv6_cidr_block                 = 
ipv6_cidr_block_association_id  = 
map_public_ip_on_launch         = false
owner_id                        = xxxxxxxxxxxx
tags.%                          = 1
tags.Name                       = terraform-sample-subnet-1a
vpc_id                          = vpc-xxxxxxxxxxxxxxxxx
  • tfstateファイルを元にtfファイルのパラメータを記述する
    • Terraformは作成したリソースのID情報を保持するため、変数で参照することで、IDをハードコーディングせずに済みます
$ cat network.tf
resource "aws_vpc" "terraform-sample-vpc" {
  cidr_block           = "10.10.0.0/16"
  enable_dns_hostnames = true

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

resource "aws_subnet" "terraform-sample-subnet-1a" {
  availability_zone = "ap-northeast-1a"
  cidr_block        = "10.10.11.0/24"
  vpc_id            = "${aws_vpc.terraform-sample-vpc.id}"

  tags {
    Name = "terraform-sample-subnet-1a"
  }
}
  • 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.

aws_vpc.terraform-sample-vpc: Refreshing state... (ID: vpc-xxxxxxxxxxxxxxxxx)
aws_subnet.terraform-sample-subnet-1a: Refreshing state... (ID: subnet-xxxxxxxxxxxxxxxxx)

------------------------------------------------------------------------

No changes. Infrastructure is up-to-date.

~省略~

おわりに

弊社にJOINする前は、管理ツールを使わないレガシーなオンプレだけに関わって来ました。今回の機会で実際に体験して、インフラをコードで管理することができるのは、すごいことだなーと感じました。一方で、単に全てコード化したとしても、費用対効果が薄い場面があるとも感じました。例えば、頻繁に同様の環境を構築する機会があったり、DRを考えてのことならコード化する価値はあると思います。コード化して何をやりたいのか明確でないと、コード化できて満足して終わり、なんてことがありそうです。とはいえ、コード化するまでに苦労はありますが、コマンド1発でインフラが構築されるのはとても気持ちが良いです。TerraformだけでなくCloudFormationも扱える両刀使いを目指してこれからも勉強していきます。

本エントリがどなたかのお役に立てれば幸いです。