TerraformでAmazon WorkSpacesを簡単インポート:importブロックとfor_eachの活用法

Tech

2024.2.7

Topics

はじめに

こんにちは、Shunです。

今回は、Amazon WorkSpacesをTerraformへ簡単にインポートする方法をご紹介します。

Terraform 1.7以降で利用可能となったimportブロック内のfor_each構文を活用して、複数のAmazon WorkSpacesを一括でインポートします。

参考: Terraform 1.7 adds test mocking and config-driven remove

importブロックの使い方については、本記事で解説をしないので、importブロックの使用方法から知りたい方は、以下の記事を先に読むことをおすすめします。

関連記事
Terraform import ブロックの実活用

想定読者

  • Terraformを使用して多数のリソースをインポートする方法に関心がある方
  • Terraformのimportブロックおよびfor_each構文の使い方に興味がある方
  • Amazon WorkSpacesの運用効率化に興味がある方

importブロック内でのfor_each構文の書き方

importブロック内でfor_each構文を使用することは、一般的なTerraformでのfor_eachの使用と大きな違いはありません。また、一般的なimportコマンドとの記述方法との違いもfor_eachの行が追加される程度です。

importブロック内でfor_each構文を利用する際の重要な点は、インポートするリソースのresourceブロックを同時に記述することです。

以下にfor_eachを用いたリソースの定義と、それに対応するインポートコマンドの生成例を示します。

locals {
  buckets = {
    "staging" = "bucket1"
    "uat"     = "bucket2"
    "prod"    = "bucket3"
  }
}

import {
  for_each = local.buckets
  to = aws_s3_bucket.this[each.key]
  id = each.value
}

# ここがポイント
resource "aws_s3_bucket" "this" {
  for_each = local.buckets
}

引用: Import multiple instances with for_each

resourceブロックを同時に記述する必要があるのは、importブロック内でfor_each構文を使用する際に、以下のようなコードの自動生成ができないためです。

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

実施すること

前提

以下を前提としています。

  • Terraform環境は構築済み
  • AWS CLI環境は構築済み
  • Python環境は構築済み
  • Amazon WorkSpacesが既に運用されている

ゴール

本記事の目的は、以下のステップを通じて、運用中のAmazon WorkSpacesをTerraformにインポートすることです。

  1. AWS CLIを使用して、運用中のAmazon WorkSpacesの一覧を取得
  2. 取得したAmazon WorkSpaces一覧を整形
  3. 整形されたAmazon WorkSpacesの一覧をTerraformへ記述し、インポートを実施

最終的なインポートを実施するためのTerraformコードは、以下の通りです。

terraform {
  required_version = "~> 1.7.0"

  backend "s3" {
    bucket         = "[s3_bucket_name]"
    key            = "[s3_key_name]"
    dynamodb_table = "[dynamodb_table_name]"
    region         = "ap-northeast-1"
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

locals {
  workspaces = {
    "[User_Name1]" = {
      DirectoryId = "[DirectoryId1]"
      BundleId    = "[BundleId1]"
      WorkspaceId = "[WorkspaceId1]"
    }
    "[User_Name2]" = {
      DirectoryId = "[DirectoryId2]"
      BundleId    = "[BundleId2]"
      WorkspaceId = "[WorkspaceId2]"
    }
    "[User_Name3]" = {
      DirectoryId = "[DirectoryId3]"
      BundleId    = "[BundleId3]"
      WorkspaceId = "[WorkspaceId3]"
    }
    ...
  }
}

import {
  for_each = local.workspaces
  to       = aws_workspaces_workspace.main[each.key]
  id       = each.value.WorkspaceId
}

resource "aws_workspaces_workspace" "main" {
  for_each     = local.workspaces
  user_name    = each.key
  directory_id = each.value.DirectoryId
  bundle_id    = each.value.BundleId
  id           = each.value.WorkspaceId
}

手順

1. AWS CLIを使用して、運用中のAmazon WorkSpacesの一覧を取得

まず、Amazon WorkSpacesをTerraformへインポートするため、以下の必要情報をAWS CLIから取得します。

  • UserName
  • DirectoryId
  • BundleId
  • WorkspaceId

次のコマンドを実行して、output.jsonへAmazon WorkSpacesの情報を出力します。

aws workspaces describe-workspaces --query "Workspaces[].[UserName,DirectoryId,BundleId,WorkspaceId]" > output.json

output.jsonは、以下のようになります。

[
    [
        "[UserName1]",
        "[DirectoryId1]",
        "[BundleId1]",
        "[WorkspaceId1]"
    ],
    [
        "[UserName2]",
        "[DirectoryId2]",
        "[BundleId2]",
        "[WorkspaceId2]"
    ],
    [
        "[UserName3]",
        "[DirectoryId3]",
        "[BundleId3]",
        "[WorkspaceId3]"
    ],
...
]

2. 取得したAmazon WorkSpaces一覧を整形

(今回は、Pythonを用いて整形してみます。)
Terraformのlocalsで指定可能なmap型へ値を整形するために、Pythonを使用します。
今回は、識別がしやすいUser_Nameを基にmapを作成します。
formatted.pyという名前のPythonファイルを作成します。

import json

# output.jsonを読み込む
with open('output.json', 'r') as file:
    data = json.load(file)

formatted_data = ""

# 各レコードをmap型に変換
for record in data:
    formatted_data += f'"{record[0]}" = {{\n'
    formatted_data += f'\tDirectoryId = "{record[1]}"\n'
    formatted_data += f'\tBundleId = "{record[2]}"\n'
    formatted_data += f'\tWorkspaceId = "{record[3]}"\n'
    formatted_data += '}\n'

print(formatted_data)

権限を付与し、実行します。

$ chmod 755 formatted.py
$ python3 formatted.py > formatted_output.json

formatted_output.jsonは以下のようになります。

    "[User_Name1]" = {
      DirectoryId = "[DirectoryId1]"
      BundleId    = "[BundleId1]"
      WorkspaceId = "[WorkspaceId1]"
    }
    "[User_Name2]" = {
      DirectoryId = "[DirectoryId2]"
      BundleId    = "[BundleId2]"
      WorkspaceId = "[WorkspaceId2]"
    }
    "[User_Name3]" = {
      DirectoryId = "[DirectoryId3]"
      BundleId    = "[BundleId3]"
      WorkspaceId = "[WorkspaceId3]"
    }
		...

3. 整形されたAmazon WorkSpacesの一覧をTerraformへ記述し、インポートを実施

2.で出力した内容をlocalsworkspacesへ入れ、それを基にimportを行います。

terraform {
  required_version = "~> 1.7.0"

  backend "s3" {
    bucket         = "[s3_bucket_name]"
    key            = "[s3_key_name]"
    dynamodb_table = "[dynamodb_table_name]"
    region         = "ap-northeast-1"
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

locals {
  workspaces = {
    # ここにpythonで整形したファイルの中身を入れます
    "[User_Name1]" = {
      DirectoryId = "[DirectoryId1]"
      BundleId    = "[BundleId1]"
      WorkspaceId = "[WorkspaceId1]"
    }
    "[User_Name2]" = {
      DirectoryId = "[DirectoryId2]"
      BundleId    = "[BundleId2]"
      WorkspaceId = "[WorkspaceId2]"
    }
    "[User_Name3]" = {
      DirectoryId = "[DirectoryId3]"
      BundleId    = "[BundleId3]"
      WorkspaceId = "[WorkspaceId3]"
    }
    ...
  }
}

import {
  for_each = local.workspaces
  to       = aws_workspaces_workspace.main[each.key]
  id       = each.value.WorkspaceId
}

resource "aws_workspaces_workspace" "main" {
  for_each     = local.workspaces
  user_name    = each.key
  directory_id = each.value.DirectoryId
  bundle_id    = each.value.BundleId
  id           = each.value.WorkspaceId
}

terraform planを実行すると、以下のようなエラーが発生しますが、これは機能が実験段階であるためであり、問題ありません。

$ terraform plan

~中略~

Plan: x to import, 0 to add, 0 to change, 0 to destroy.
╷
│ Warning: Config generation is experimental
│ 
│ Generating configuration during import is currently experimental, and the generated

このエラーは無視してterraform applyを実行します。

$ terraform apply

~中略~
 
Apply complete! Resources: x imported, 0 added, 0 changed, 0 destroyed.

これでインポートは完了です。

$ terraform state list
aws_workspaces_workspace.main["User_Name1"]
aws_workspaces_workspace.main["User_Name2"]
aws_workspaces_workspace.main["User_Name3"]
...

まとめ

Terraform 1.7では、新たにimportブロック内でfor_each構文が使用できるようになりました。

この新機能のおかげで、importブロックにリソースを一つずつ記述する必要がなくなりました。これにより、コード化のプロセスがより効率的になります。

しかし、冒頭の関連記事で紹介されている、従来のimportコマンドで利用できたコード生成機能はこの新機能ではサポートされていません。

そのため、インポート自体は効率的になっても、Terraformの管理自体は煩雑になる可能性があります。

Amazon WorkSpacesの運用を考えると、以下のようなコード化が望ましい場合があります。

$ cat import.tf
import {
  to       = aws_workspaces_workspace.User_Name1
  id       = each.value.WorkspaceId
}

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

$ cat generated.tf
resource "aws_workspaces_workspace" "User_Name1" {
  bundle_id                      = "BundleId1"
  directory_id                   = "DirectoryId1"
  root_volume_encryption_enabled = false
  tags                           = {}
  tags_all                       = {}
  user_name                      = "User_Name1"
  user_volume_encryption_enabled = false
  volume_encryption_key          = null
  workspace_properties {
    compute_type_name                         = ""
    root_volume_size_gib                      = 
    running_mode                              = ""
    running_mode_auto_stop_timeout_in_minutes = 
    user_volume_size_gib                      = 
  }
}

今後、様々な検証を行いながら、この新しい機能の適切な使用方法を模索していく必要があります。

最後まで読んでいただき、ありがとうございました!

Shun

好きな言葉は生ビール199円です。日本ビール協会認定1冠

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら