🔰

Terraformを利用していなかった過去の自分へ送る

2024/10/29に公開

はじめに

お世話になっております。primeNumberの庵原です。
秋雨の候、いかがお過ごしでしょうか。

今回は今までAWSのデプロイをAWS CLIで行っていたものをTerraformに移管した際の感動を皆様にお伝えできればと思っています。

対象

  • Terraform自体を知っているけど、使ったことがない方
  • Terraformのメリットがあまり把握できていない方

Terraformについての説明

概要

https://www.terraform.io/

HashiCorpが開発したオープンソースのIaC(Infrastructure as Code)ツールで、主にクラウド環境におけるインフラストラクチャをコードで管理・自動化するために利用するツールです。

AWS Provider

https://registry.terraform.io/providers/hashicorp/aws/latest/docs

TerraformでAWSのリソースを記述する際に、Terraform側で既に用意されているProviderを用いることができます。これを使うと定義を簡単に記述することができます。
例として、下記は公式ドキュメントに記載されているもので、VPCを作成する記述です。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
# Configure the AWS Provider
provider "aws" {
  version = "~> 5.0"
  region  = "us-east-1"
}

# Create a VPC
resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"
}

Create a VPCのたった3行の記述でVPCの設定を定義できるのです!

移管の背景

AWS上で作成していたPythonスクリプトをECSで実行する仕組みがあったのですが、私がこのプロジェクトにJoinした段階では、AWS CLIで全てデプロイが記載されていました。

AWS CLIの例

AWS CLIの一部
# ##########################
# VPCの作成と タグ付け
VPC_ID=$(aws ec2 create-vpc \
    --cidr-block $VPC_CIDR \
    --query "Vpc.VpcId" \
    --output text)
aws ec2 create-tags \
    --resources $VPC_ID \
    --tags Key=Name,Value=${ENVIRONMENT}-${PROJECT}-vpc

# ##########################
# Public Subnetの作成とタグ付け
PUBLIC_SUBNET_ID=$(aws ec2 create-subnet \
    --vpc-id $VPC_ID \
    --cidr-block $PUBLIC_SUBNET_CIDR \
    --availability-zone $AVAILABILITY_ZONE \
    --query "Subnet.SubnetId" \
    --output text)
aws ec2 create-tags \
    --resources $PUBLIC_SUBNET_ID \
    --tags Key=Name,Value=${ENVIRONMENT}-${PROJECT}-public-subnet-a Key=Project,Value=$PROJECT Key=Environment,Value=$ENVIRONMENT

...

デプロイの手順の多さ

一部の例でお見せしたのはネットワーク周りの設定ですが、これ以外にもIAMロール・ポリシーの設定、ECRのリポジトリ、ECRへのpush、SecretsManager、EventBridge(rule ×10くらい)、ECS、CloudWatchなど様々なサービスを利用しており、Pythonスクリプトが動くだけとはいえ、なかなかの大御所になっていました。

その上でこれを再度検証環境 → 本番環境で機能改修が行われるたびに影響範囲を調べてデプロイし直すのはかなり大変なことでした。とはいえクイックスタート・スモールスタートで始める際はとりあえずCLI経由で試しながら進める方がやりやすいのは間違いないと考えています。

状態管理

AWS上で実際にデプロイしたリソース同士がどのようなIDで繋がっているか、そもそもリソースが今どのような状況か、デプロイする際に過去のリソースとのdiffがどうなるかなど、特に後から入るメンバーにとってはAWSのコンソールをまじまじとみて把握していくには限界があるかと思います。
実際に、AWS CLIの記述はとても整理された状態ではあったのですが、そのような状況でもどの記述がそもそもデプロイに関与しているのかも理解するにはなかなか時間がかかってしまっていました。

移管の手順

LLMの利用

元も子もないですが、先述の通り、AWS CLIの記述はとても整理された状態であったので、AWS CLI→Terraformへの記述変換を1つずつ行っていきました。
一部バージョンによる差異があったため、実際にAWS Providerのresource定義を見ながら対応を進めました。

module分け

AWS複数のサービスを使ってい他ため、単一のmain.tfに全て記述するのは保守の観点からも微妙かな〜と思っていた中で、下記の記事に出会いました。

https://qiita.com/m-oka-system/items/e69d6bb86eae74619a27

特にディレクトリ構成の部分は私にもわかりやすく、環境分離やmoduleの使い回し、値の受け渡しも簡潔に書ける点からこの方針を採用しました。

最終的なディレクトリ構成は以下のようになりました。

ディレクトリ構成
.
├── ecr
│   ├── main.tf
│   ├── output.tf
│   └── variable.tf
├── ecs
│   ├── main.tf
│   ├── output.tf
│   └── variable.tf
├── env
│   ├── dev
│   │   ├── imports.tf
│   │   ├── main.tf
│   │   ├── provider.tf
│   │   └── variables.tf
│   └── prd
│       ├── imports.tf
│       ├── main.tf
│       ├── provider.tf
│       └── variables.tf
├── eventbridge
│   ├── main.tf
│   ├── output.tf
│   └── variable.tf
├── iam_role
│   ├── main.tf
│   ├── output.tf
│   └── variable.tf
├── log
│   ├── main.tf
│   ├── output.tf
│   └── variable.tf
├── secretsmanager
│   ├── main.tf
│   ├── output.tf
│   └── variable.tf
└── vpc
    ├── main.tf
    ├── output.tf
    └── variable.tf

(ほぼまるパクリ)

既存デプロイ済みの状態のimport

Terraformにはimportという機能があり、現在デプロイしているリソースをTerraformを記述したディレクトリ(また状態管理用ファイル)に取り込むことができます。
やり方は主に2つあり、terraform importコマンドを使って1つずつimportする方法と、imports.tfファイルを使って定義を引っこ抜いてくる方法です。

そこそこの量があったので、今回はimports.tfを用いた方法を取りました。

書き方の例は以下のとおりです。

imports.tf
## VPC
data "aws_vpc" "imported" {
  filter {
    name   = "tag:Name"
    values = [var.vpc_name]
  }
}

import {
  to = module.vpc.aws_vpc.main
  id = data.aws_vpc.imported.id
}

## route table
data "aws_route_table" "public_import" {
  filter {
    name   = "tag:Name"
    values = [var.route_table_public_name]
  }
}

data "aws_route_table" "private_import" {
  filter {
    name   = "tag:Name"
    values = [var.route_table_private_name]
  }
}

data blockを用いて情報を取得し、import blockで実際のimport処理を行う形です。
これを行うと、terraform.tfstateというファイルが出来上がり、現在のデプロイ状況の状態管理をファイルで行うことができます。

Terraformに乗り換えたことによる嬉しいこと

多分ありきたりなことしか言ってない気がしますが、改めて

diff調査が簡単すぎる

リソースに対する変更を行う際の今の状態と変更後の状態をterrform planというコマンドで見ることができるのですが、それが本当に楽です。

  • 勝手に新規作成していないか
  • 変更を想定している部分だけが更新されるようになっているか

特に上の2つ観点から作業が楽になったな〜と実感しています。

複数人での開発が楽になった

GitHubでのコード管理も行っているのですが、バージョン管理とTerraformの相性が抜群なのを身をもって体験しました。
特にレビューのしやすさ・変更の影響範囲をレビュアーがチェックするのもローカルに落として確認するだけなのでとても楽になりました。

検証が楽になった

開発環境で自分専用のリソースを作ってAWS上で操作したい場合、Terraform内で管理している変数を書き換えることで、自分が作成した一時的な名前でリソースを作成することができるようになりました。
また、これも勝手に本番のリソースを上書きしていないかも確認できますし、追加でterraform destroyコマンドを用いることで簡単に検証用に作ったリソースだけを削除することも可能です。

私はよくterraform apply -target=module.ecsterraform destroy -target=module.ecsという形でモジュール単位で作成・削除を行うことが多かったです。

今後のやりたいこと

CI/CD化

Terraformを利用する上での最大のメリットは、CI/CDを自動化することも大きな要因かと思われます。GitHubであればGitHub Actionsを用いることが多いかと思いますが、ぜひこちらも試してみたいと思っています。

まとめ

Terraform自体は日本語でも情報がたくさんネット上にありますが、改めて何が嬉しいのかを叫んでいる記事があまりなかったので、ネットの中心でTerraformへの愛を叫んでみました。

この記事が読んだあなたのためになれば嬉しいです。

株式会社primeNumber

Discussion