とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど

開催予定の勉強会

読者登録と各種SNSのフォローもよろしくお願いいたします。

terraform-docsでドキュメントの自動生成

IaCでインフラを管理していると、構築手順書が不要になります。構築手順がコードで表現されているからです。では、一切のドキュメントが不要かというと、そんなことはありません。設定値の説明などは必要になるので、使い方のドキュメントは必要になります。

かんたんDevOpsではIaCツールとしてTerraformを使っているんですが、terraform-docsというツールを使うと、そこそこのドキュメントがツールによって生成できることがわかりました。っということで、さっそく使ってみます。

terraform-docs???

terraform-docs.io

github.com

Terraformモジュールからドキュメントを生成するツールです。使っているモジュールがどのバージョンに依存しているのか、Input/Outputに渡すべき値なんかはわざわざ手で修正していると面倒ですし、修正漏れの原因になります。コードから自動で収集できる情報は自動化しておきたいですよね。

インストール

Installation | terraform-docs

パッケージマネージャを使うか、コンテナを使うか、バイナリを直接ダウンロードするか、go install go getするとよさそうです。私の検証環境はMacなのでHomebrewを利用してインストールしました。

使ってみる

今回は対象の環境にplanしたりapplyしたりはしないので、適当なHCLを用意します。

$ tree .
.
├── main.tf
├── outputs.tf
├── providers.tf
└── variables.tf

1 directory, 4 files

main.tf

resource "random_string" "random" {
  length  = 4
  lower   = true
  upper   = false
  numeric = true
  special = false
}

locals {
  # var.nameがnullだったらインスタンス名を`neko-${random_string}`にする
  name = var.name == null ? "neko-${random_string.random.result}" : var.name
}

module "ec2_instance" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "4.3.0"

  name = local.name

  ami           = var.ami_id
  instance_type = var.instance_type
  monitoring    = var.monitoring
  subnet_id     = var.subnet_id
}

outputs.tf

output "instance_id" {
  description = "インスタンスID"
  value       = module.ec2_instance.id
}

providers.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.57.1"
    }
  }
}

variables.tf

  type        = string
  default     = null
  description = "インスタンス名"
}

variable "ami_id" {
  type        = string
  description = "AMI ID. AMI IDの取得方法は[こちら](#ami-idを取得)"
}

variable "instance_type" {
  type        = string
  description = "インスタンスタイプ. インスタンスタイプの一覧は[こちら](https://aws.amazon.com/jp/ec2/instance-types/)"
}

variable "monitoring" {
  type        = bool
  default     = false
  description = "EC2のモニタリングを有効化するか. モニタリングの説明については[こちら](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/monitoring_ec2.html)"
}

variable "subnet_id" {
  type        = string
  description = "EC2を起動するサブネットを指定. サブネットIDの取得方法は[こちら](#subnet-idを取得)"
}

カレントディレクトリのREADME.mdを生成してみます。

terraform-docs markdown table --output-file README.md --output-mode inject .

サブコマンドのmarkdownはMarkdownフォーマットのドキュメントを生成するという意味です。他にもJSONやTOMLなど、様々なフォーマットに対応しています。どんなフォーマットに対応しているかはterraform-docs --helpで確認できます。

markdownサブコマンドの引数にtableを指定しています。テーブルでinput/outputの説明が出力されますtable以外にはdocumentがあります。markdownサブコマンドのヘルプはterraform markdown --helpで確認できます。

--output-fileは文字通り出力先のファイル名です。今回はREADME.mdファイルに出力します。

--output-modeinjectを指定しています。inject<!-- BEGIN_TF_DOCS -->から<!-- END_TF_DOCS -->の間を置き換えてくれます。つまり、生成されたドキュメント以外(手書きのものなど)は<!-- {BEGIN,END}_TF_DOCS -->外に置いておけば維持できます。injectの他にはreplaceがありますが、これは全て置き換わってしまいます。ドキュメントに人の手を入れたくない時などに使えばいいでしょうか。

その他のオプションもterraform-docs markdown --helpで確認できますので、ご確認ください。

実際に生成されたREADME.mdは以下

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | 4.57.1 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_random"></a> [random](#provider\_random) | 3.4.3 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_ec2_instance"></a> [ec2\_instance](#module\_ec2\_instance) | terraform-aws-modules/ec2-instance/aws | 4.3.0 |

## Resources

| Name | Type |
|------|------|
| [random_string.random](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_ami_id"></a> [ami\_id](#input\_ami\_id) | AMI ID. AMI IDの取得方法は[こちら](#ami-idを取得) | `string` | n/a | yes |
| <a name="input_instance_type"></a> [instance\_type](#input\_instance\_type) | インスタンスタイプ. インスタンスタイプの一覧は[こちら](https://aws.amazon.com/jp/ec2/instance-types/) | `string` | n/a | yes |
| <a name="input_monitoring"></a> [monitoring](#input\_monitoring) | EC2のモニタリングを有効化するか. モニタリングの説明については[こちら](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/monitoring_ec2.html) | `bool` | `false` | no |
| <a name="input_name"></a> [name](#input\_name) | インスタンス名 | `string` | `null` | no |
| <a name="input_subnet_id"></a> [subnet\_id](#input\_subnet\_id) | EC2を起動するサブネットを指定. サブネットIDの取得方法は[こちら](#subnet-idを取得) | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_instance_id"></a> [instance\_id](#output\_instance\_id) | インスタンスID |
<!-- END_TF_DOCS -->

プレビューしてみるとこんな感じ

今度はterraform.tfvarsを生成してみます。

terraform-docs tfvars hcl --output-file terraform.tfvars --output-template "// BEGIN_TF_DOCS\n{{ .Content }}\n// END_TF_DOCS" .

今回、--output-templateを指定して<!-- BEGIN_TF_DOCS -->// BEGIN_TF_DOCSに変更しています。tfvarsのコメントフォーマットに合わせた感じですね。いやいや、こうやったらいいんじゃね?っというのがあれば教えて欲しいです。

ちなみに、以下のようにするとコメントはつかなくなりました。初回生成するだけならこれでもいいかもしれないです。

terraform-docs tfvars hcl . > terraform.tfvars

まとめ

細かい設定なんかは割愛しましたが、ドキュメントを生成したいだけならこれで十分です。もし、もう少し凝ったことをやりたい場合はこちらを参考にしてみてください。GitHubの方のドキュメントにはGitのpre-commitで実行する方法や、GitHub Actionsを使って自動で生成する方法も記載されています。