😊

構築済みのaws環境をterraform化できたのでtipsを紹介したい

に公開

はじめに

業務でAWS環境をコンソール内で構築したのですが、terraformでコード管理できるようにしたいという要望があり、terraform化することになりました。
AWSの経験3ヶ月くらい、terraformは未経験ながら、なんとかterraform化にこぎつけることができました。そのときに便利だと感じたtipsを紹介します。

importブロック

importブロックを使って既存のAWS環境をimportします。
importしたいリソースのimportブロックをimport.tfに作成します。
そしてterraform planコマンドを実行してterraform applyでtfstateにimportが完了します。

import {
  to = aws_s3_bucket.example
  id = "my-existing-bucket"
}

以下でimport時のterrafom planについて解説します。

terraform plan

terraform planコマンドで実行計画を確認します。plan結果に問題がなければapplyをして、tfstateファイルとして保存します。
また、コマンドのオプションを指定することでimport方法を選択することができます。

私は以下の2つのimportのやり方で既存リソースのterraform化を行いました。

  • オプションなし
  • -generate-config-outオプション

オプションなし

オプションを指定しない場合、resourceブロックをあらかじめ定義している必要があります。
そしてplanの実行結果を参考に、resourceブロックの定義を修正しながら差分を埋めていきます。

import.tf
import {
  to = aws_s3_bucket.example
  id = "my-existing-bucket"
}
s3.tf
resource "aws_s3_bucket" "example" {
  bucket = "my-existing-bucket"
  acl    = "private"
  # 必要に応じて以下のような属性を追加しておくと差分が少なくなる
  # force_destroy = false
  # versioning {
  #   enabled = false
  # }
}
# plan結果を見ながらresourceを構成して差分を埋めていく
terraform plan

plan結果が問題なければ、terraform applyをしてtfstateファイルを作成し、import作業を完了させます。
import完了後はimportブロックを削除あるいはコメントアウトさせる必要があります。

このimport方法は、以下のときに有効です。

  • すでにある程度のモジュール化を終えたterraformを流用して別のリソースをimportしたいとき
  • 開発環境は済んでいて、そこから本番環境に展開したい。

-generate-config-outオプションによる自動生成

terraform plan -generate-config-out=generated.tf

このオプションを付与することでimportしたいリソースが自動生成されます。

generated.tf
resource "aws_s3_bucket" "example" {
  bucket = "my-existing-bucket"
  acl    = "private"

  tags = {
    "Name" = "my-existing-bucket"
  }
}

その後、生成されたファイルに問題なければterraform applyをしてtfstateファイルを作成し、import作業を完了させます。
import完了後はimportブロックを削除あるいはコメントアウトさせる必要があります。
大変便利ですが、ハードコードなデータなので複雑なAWS環境のterraform化となると、適宜変数化やモジュール化を行う必要があります。
ハードコードをimportしてモジュール化して・・・となると結構大変です。

このimport方法は以下のときに便利です。

  • まったくの0からリソースをterraform化するとき

terraform_remote_state

terraform化したリソースたちに依存関係を持たせるために使います。
例えばECSのtask_definitionを定義する時にtaskExecutionRoleとtaskRoleに対象となるiamのarnをiamのremote_stateを作成することでiamのarnを変数化するという感じです。

iamの情報を他リソースで共有するために、iamにoutputブロックを作成します。

# filepath: iam/outputs.tf
output "task_role_arn" {
  value       = aws_iam_role.task_role.arn
  description = "ARN of the ECS task role"
}

output "task_execution_role_arn" {
  value       = aws_iam_role.task_execution_role.arn
  description = "ARN of the ECS task execution role"
}

outputを作成したら、一度terraform applyをしてtfstateにoutputを登録させる必要があります。

# 実行計画の確認
terraform plan
# 問題なければapply
terraform apply

ECSにiamのremote_stateブロックを作成します。
remote_stateを経由することで、iamの情報をECSは受け取ることができます。

# filepath: ecs/main.tf
data "terraform_remote_state" "iam" {
  backend = "s3"
  config = {
    bucket = "my-terraform-state-bucket"
    key    = "iam/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

resource "aws_ecs_task_definition" "app" {
  family                   = "app-task"
  # iamのoutputで定義した変数が使える
  task_role_arn            = data.terraform_remote_state.iam.outputs.task_role_arn
  # iamのoutputで定義した変数が使える
  execution_role_arn       = data.terraform_remote_state.iam.outputs.task_execution_role_arn
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]

  container_definitions = jsonencode([
    {
      name  = "app"
      image = "amazonlinux:2"
      cpu   = 256
      memory = 512
      essential = true
      portMappings = [{ containerPort = 80, hostPort = 80 }]
    }
  ])
}

state管理

tfstateの確認をするときに使うコマンドを紹介します。

state show

既存ステート内のリソースの詳細を表示します。リソースの属性確認や、import後の確認に便利です。

# 例: S3 バケットのステート詳細を表示
terraform state show aws_s3_bucket.example

# 出力例
id = my-existing-bucket
arn = arn:aws:s3:::my-existing-bucket
bucket = my-existing-bucket
acl = private
...

state list

現在の tfstate に登録されている全リソースのアドレス一覧を取得します。

terraform state list

# 出力例:
# aws_s3_bucket.example
# aws_iam_role.task_role
# module.ecs.aws_ecs_task_definition.app

state rm

tfstate からリソースを削除します。コンソール上から実際にリソースが削除されるわけではなく、あくまでtfstateの管理から離すだけです。

# 例: tfstate から削除
terraform state rm aws_s3_bucket.example

以下のときに使います。

  • 誤ってimportしてしまったリソースをstateから外す
  • 外部管理に移行するリソースのstateを切り離す

state mv

resourceの名前を変更した際、紐づいているtfstateのリソース名も変更するのに使います。

terraform state mv aws_s3_bucket.example aws_s3_bucket.renamed_example

movedブロック

上記のterraform state mvと用途は同じです。
しかし、このブロックを使うことでリネームの履歴をコード上に残すことができます。

# 例: moved ブロック
moved {
  from = aws_s3_bucket.example
  to   = aws_s3_bucket.renamed_example
}

おわりに

terraform、最初は全然意味わからなかったのですが、理解していくごとにAWSの使用への理解も深めることができたのでやってよかったです。

Discussion