

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# コードベースの構造と組織のベストプラクティス
<a name="structure"></a>

大規模なチームや企業で Terraform の使用が増加するにつれて、適切なコードベース構造と組織ことが重要です。優れた設計のコードベースにより、大規模なコラボレーションが可能になり、保守性が向上します。

このセクションでは、品質と一貫性をサポートする Terraform のモジュール性、命名規則、ドキュメント、コーディング標準に関する推奨事項を提供します。

ガイダンスには、環境とコンポーネント別の再利用可能なモジュールへの設定の分割、プレフィックスとサフィックスを使用した命名規則の確立、モジュールの文書化と入出力の明確な説明、自動スタイルチェックを使用した一貫したフォーマットルールの適用が含まれます。

その他のベストプラクティスでは、モジュールとリソースを構造化階層に論理的に整理し、ドキュメントでパブリックモジュールとプライベートモジュールをカタログ化し、モジュールで不要な実装の詳細を抽象化して使用を簡素化します。

モジュール性、ドキュメント、標準、論理的な組織に関するコードベース構造のガイドラインを実装することで、組織全体に使用量が分散するにつれて Terraform を維持したまま、チーム間の広範なコラボレーションをサポートできます。規則と標準を適用することで、フラグメント化されたコードベースの複雑さを回避できます。

**Topics**
+ [標準リポジトリ構造を実装する](#repo-structure)
+ [モジュール性の構造](#modularity)
+ [命名規則に従う](#naming-conventions)
+ [アタッチメントリソースを使用する](#attachment-resources)
+ [デフォルトのタグを使用する](#default-tags)
+ [Terraform レジストリ要件を満たす](#registry-reqs)
+ [推奨されるモジュールソースを使用する](#module-sources)
+ [コーディング標準に従う](#coding-standards)

## 標準リポジトリ構造を実装する
<a name="repo-structure"></a>

次のリポジトリレイアウトを実装することをお勧めします。これらの整合性プラクティスをモジュール間で標準化することで、検出可能性、透明性、整理、信頼性が向上し、多くの Terraform 設定で再利用できます。
+ **ルートモジュールまたはディレクトリ**: これは Terraform [ルート](https://developer.hashicorp.com/terraform/language/files#the-root-module)モジュールと[再利用可能な](https://developer.hashicorp.com/terraform/language/modules/develop)モジュールの両方のプライマリエントリポイントであり、一意であることが期待されます。より複雑なアーキテクチャがある場合は、ネストされたモジュールを使用して軽量な抽象化を作成できます。これにより、物理オブジェクトの観点からではなく、アーキテクチャの観点からインフラストラクチャを記述できます。
+ **README**: ルートモジュールとネストされたモジュールには README ファイルが必要です。このファイルの名前は である必要があります`README.md`。これには、モジュールの説明と使用目的が含まれている必要があります。このモジュールを他のリソースで使用する例を含める場合は、 `examples` ディレクトリに配置します。モジュールが作成するインフラストラクチャリソースとその関係を示す図を含めることを検討してください。[terraform-docs](https://github.com/terraform-docs/terraform-docs) を使用して、モジュールの入力または出力を自動的に生成します。
+ **main.tf**: これはプライマリエントリポイントです。シンプルなモジュールの場合、すべてのリソースがこのファイルに作成される場合があります。複雑なモジュールの場合、リソースの作成は複数のファイルに分散される場合がありますが、ネストされたモジュールの呼び出しは `main.tf` ファイル内に存在する必要があります。
+ **variables.tf** および **outputs.tf**: これらのファイルには、変数と出力の宣言が含まれています。すべての変数と出力には、その目的を説明する 1 文または 2 文の説明が必要です。これらの説明はドキュメントに使用されます。詳細については、[変数設定](https://developer.hashicorp.com/terraform/language/values/variables)と[出力設定](https://developer.hashicorp.com/terraform/language/values/outputs)の HashiCorp ドキュメントを参照してください。
  + すべての変数には定義された型が必要です。
  + 変数宣言には、デフォルトの引数を含めることもできます。宣言にデフォルトの引数が含まれている場合、変数はオプションと見なされ、モジュールを呼び出すか Terraform を実行するときに値を設定しない場合、デフォルト値が使用されます。デフォルトの引数にはリテラル値が必要であり、設定内の他のオブジェクトを参照することはできません。変数を必須にするには、変数宣言でデフォルトを省略し、 設定`nullable = false`が理にかなっているかどうかを考慮します。
  + 環境に依存しない値 ( など`disk_size`) を持つ変数には、デフォルト値を指定します。
  + 環境固有の値 ( など`project_id`) を持つ変数の場合は、デフォルト値を指定しないでください。この場合、呼び出し元のモジュールは意味のある値を指定する必要があります。
  + 空の文字列やリストなどの変数には、変数を空のままにすることが、基になる APIs が拒否しない有効な設定である場合にのみ、空のデフォルトを使用します。
  + 変数の使用には慎重を期してください。値は、インスタンスまたは環境ごとに変更する必要がある場合にのみパラメータ化します。変数を公開するかどうかを決定するときは、その変数を変更するための具体的なユースケースがあることを確認してください。変数が必要になる可能性がわずかしかない場合は、公開しないでください。
    + デフォルト値を持つ変数を追加すると、下位互換性があります。
    + 変数の削除には下位互換性がありません。
    + リテラルが複数の場所で再利用される場合は、変数として公開せずにローカル値を使用する必要があります。
  + 入力変数は依存関係グラフに適切に追加されないため、出力を直接渡さないでください。[暗黙的な依存関係](https://learn.hashicorp.com/terraform/getting-started/dependencies.html)が作成されるように、 がリソースから属性を参照するようにします。インスタンスの入力変数を直接参照する代わりに、 属性を渡します。
+ **locals.tf**: このファイルには、式に名前を割り当てるローカル値が含まれているため、式を繰り返す代わりに、モジュール内で名前を複数回使用できます。ローカル値は、関数の一時的なローカル変数のようなものです。ローカル値の式はリテラル定数に制限されません。変数、リソース属性、その他のローカル値など、モジュール内の他の値を参照して組み合わせることもできます。
+ **providers.tf**: このファイルには、[Terraform ブロック](https://www.terraform.io/language/block/terraform)と[プロバイダーブロック](https://developer.hashicorp.com/terraform/language/providers/configuration#provider-configuration-1)が含まれています。 `provider`ブロックは、モジュールのコンシューマーがルートモジュールでのみ宣言する必要があります。

  HCP Terraform を使用している場合は、空の[クラウドブロック](https://developer.hashicorp.com/terraform/cli/cloud/settings#the-cloud-block)も追加します。`cloud` ブロックは、CI/CD パイプラインの一部として[、環境変数](https://developer.hashicorp.com/terraform/cli/cloud/settings#environment-variables)と[環境変数認証情報](https://developer.hashicorp.com/terraform/cli/config/config-file#environment-variable-credentials)を使用して完全に設定する必要があります。
+ **versions.tf**: このファイルには [required\$1providers](https://developer.hashicorp.com/terraform/language/providers/requirements#requiring-providers) ブロックが含まれています。すべての Terraform モジュールは、Terraform がこれらのプロバイダーをインストールして使用できるように、必要なプロバイダーを宣言する必要があります。
+ **data.tf**: シンプルな設定の場合は、[データソースを参照するリソースの](https://developer.hashicorp.com/terraform/language/data-sources)横にデータソースを配置します。たとえば、インスタンスの起動に使用するイメージを取得する場合は、独自のファイルにデータリソースを収集するのではなく、インスタンスと一緒に配置します。データソースの数が多すぎる場合は、専用`data.tf`ファイルに移動することを検討してください。
+ **.tfvars ファイル**: ルートモジュールの場合、 `.tfvars` ファイルを使用して、機密性のない変数を指定できます。整合性を保つには、変数ファイルに という名前を付けます`terraform.tfvars`。リポジトリのルートに共通の値を配置し、 `envs/`フォルダ内に環境固有の値を配置します。
+ **ネストされたモジュール**: ネストされたモジュールは `modules/`サブディレクトリに存在する必要があります。を持つネストされたモジュールは`README.md`、外部ユーザーが使用できると見なされます。`README.md` が存在しない場合、モジュールは内部使用のみと見なされます。ネストされたモジュールを使用して、複雑な動作をユーザーが慎重に選択して選択できる複数の小さなモジュールに分割する必要があります。

  ルートモジュールにネストされたモジュールへの呼び出しが含まれている場合、これらの呼び出しは、Terraform がそれらを個別にダウンロードするのではなく、同じリポジトリまたはパッケージの一部と見な`./modules/sample-module`すように、 などの相対パスを使用する必要があります。

  リポジトリまたはパッケージに複数のネストされたモジュールが含まれている場合は、相互に直接呼び出してモジュールの深いネストされたツリーを作成するのではなく、呼び出し元が構成できるのが理想的です。
+ **例**: 再利用可能なモジュールを使用する例は、リポジトリのルートの `examples/`サブディレクトリに存在する必要があります。各例について、README を追加して、例の目標と使用方法を説明することができます。サブモジュールの例は、ルート`examples/`ディレクトリにも配置する必要があります。

  多くの場合、例はカスタマイズのために他のリポジトリにコピーされるため、モジュールブロックのソースは、相対パスではなく、外部発信者が使用するアドレスに設定する必要があります。
+ **サービス名付きファイル**: ユーザーは多くの場合、複数のファイルでサービスごとに Terraform リソースを分離します。この方法はできるだけ推奨せず、`main.tf`代わりに でリソースを定義する必要があります。ただし、リソースのコレクション (IAM ロールやポリシーなど) が 150 行を超える場合は、 などの独自のファイルに分割することをお勧めします`iam.tf`。それ以外の場合は、すべてのリソースコードを で定義する必要があります`main.tf`。
+ **カスタムスクリプト**: 必要な場合にのみスクリプトを使用します。Terraform は、スクリプトによって作成されたリソースの状態を考慮または管理しません。Terraform リソースが目的の動作をサポートしていない場合にのみ、カスタムスクリプトを使用します。Terraform によって呼び出されたカスタムスクリプトを `scripts/` ディレクトリに配置します。
+ **ヘルパースクリプト**: ディレクトリで Terraform によって呼び出されないヘルパースクリプトを整理します`helpers/`。説明と呼び出し例を使用して、 `README.md` ファイルにヘルパースクリプトをドキュメント化します。ヘルパースクリプトが引数を受け入れる場合は、引数チェックと`--help`出力を指定します。
+ **静的ファイル**: Terraform が参照するが実行しない静的ファイル (EC2 インスタンスにロードされたスタートアップスクリプトなど) は、 `files/` ディレクトリに整理する必要があります。HCL とは別に、外部ファイルに長いドキュメントを配置します。[file() 関数](https://www.terraform.io/language/functions/file)で参照します。
+ **テンプレート**: Terraform [templatefile 関数](https://www.terraform.io/docs/configuration/functions/templatefile.html)が読み取るファイルには、ファイル拡張子 を使用します`.tftpl`。テンプレートは `templates/` ディレクトリに配置する必要があります。

### ルートモジュール構造
<a name="root-module"></a>

Terraform は常に単一のルートモジュールのコンテキストで実行されます。完全な Terraform 設定は、ルートモジュールと子モジュールのツリー (ルートモジュールによって呼び出されるモジュール、それらのモジュールによって呼び出されるモジュールなどを含む) で構成されます。

Terraform ルートモジュールレイアウトの基本的な例:

```
.
├── data.tf
├── envs
│   ├── dev
│   │   └── terraform.tfvars
│   ├── prod
│   │   └── terraform.tfvars
│   └── test
│       └── terraform.tfvars
├── locals.tf
├── main.tf
├── outputs.tf
├── providers.tf
├── README.md
├── terraform.tfvars
├── variables.tf
└── versions.tf
```

### 再利用可能なモジュール構造
<a name="module-structure"></a>

再利用可能なモジュールは、ルートモジュールと同じ概念に従います。モジュールを定義するには、ルートモジュールを定義するのと同様に、新しいディレクトリを作成し、その中に`.tf`ファイルを配置します。Terraform は、ローカル相対パスまたはリモートリポジトリからモジュールをロードできます。モジュールが多くの設定で再利用されることが予想される場合は、独自のバージョン管理リポジトリに配置します。さまざまな組み合わせでモジュールを簡単に再利用できるように、モジュールツリーを比較的平らに保つことが重要です。

Terraform 再利用可能なモジュールレイアウトの基本例:

```
.
├── data.tf
├── examples
│   ├── multi-az-new-vpc
│   │   ├── data.tf
│   │   ├── locals.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── providers.tf
│   │   ├── README.md
│   │   ├── terraform.tfvars
│   │   ├── variables.tf
│   │   ├── versions.tf
│   │   └── vpc.tf
│   └── single-az-existing-vpc
│   │   ├── data.tf
│   │   ├── locals.tf
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   ├── providers.tf
│   │   ├── README.md
│   │   ├── terraform.tfvars
│   │   ├── variables.tf
│   │   └── versions.tf
├── iam.tf
├── locals.tf
├── main.tf
├── outputs.tf
├── README.md
├── variables.tf
└── versions.tf
```

## モジュール性の構造
<a name="modularity"></a>

原則として、任意のリソースやその他のコンストラクトをモジュールに結合できますが、ネストされた再利用可能なモジュールを過剰に使用すると、Terraform 設定全体の理解と維持が困難になる可能性があるため、これらのモジュールをモデレーションで使用します。

理にかなっている場合は、リソースタイプから構築されたアーキテクチャの新しい概念を記述することで、抽象化のレベルを高める再利用可能なモジュールに構成を分割します。

インフラストラクチャを再利用可能な定義にモジュール化する場合は、個々のコンポーネントや過度に複雑なコレクションではなく、リソースの論理セットを目指します。

### 単一のリソースをラップしない
<a name="single-resources"></a>

他の単一のリソースタイプを囲む薄いラッパーのモジュールを作成しないでください。モジュール内のメインリソースタイプの名前とは異なるモジュールの名前を見つけられない場合、モジュールが新しい抽象化を作成していない可能性があります。不要な複雑さが発生しています。代わりに、呼び出し元のモジュールで リソースタイプを直接使用します。

### 論理関係をカプセル化する
<a name="logical-relationships"></a>

ネットワーク基盤、データ層、セキュリティコントロール、アプリケーションなどの関連リソースのグループセット。再利用可能なモジュールは、機能を有効にするために連携するインフラストラクチャ部分をカプセル化する必要があります。

### 継承を平らに保つ
<a name="inheritance"></a>

モジュールをサブディレクトリにネストする場合は、1 つまたは 2 つのレベルを深くしないようにします。深くネストされた継承構造は、設定とトラブルシューティングを複雑にします。モジュールは他のモジュール上に構築する必要があります。それらを介してトンネルを構築しないでください。

アーキテクチャパターンを表す論理リソースグループに焦点を当てることで、チームは信頼性の高いインフラストラクチャ基盤をすばやく設定できます。過剰エンジニアリングや過剰簡素化なしで抽象化のバランスを取ります。

### 出力のリソースを参照する
<a name="output-resources"></a>

再利用可能なモジュールで定義されているリソースごとに、リソースを参照する出力を少なくとも 1 つ含めます。変数と出力を使用すると、モジュールとリソース間の依存関係を推測できます。出力がないと、ユーザーは Terraform 設定に関連してモジュールを適切に注文できません。

環境の一貫性、目的主導のグループ化、エクスポートされたリソースリファレンスを提供する構造化されたモジュールにより、組織全体の Terraform コラボレーションを大規模に実現できます。チームは、再利用可能な構成要素からインフラストラクチャを組み立てることができます。

### プロバイダーを設定しない
<a name="configuration"></a>

共有モジュールはモジュールの呼び出しからプロバイダーを継承しますが、モジュールはプロバイダー設定自体を設定しないでください。モジュールでプロバイダー設定ブロックを指定しないでください。この設定はグローバルに 1 回だけ宣言する必要があります。

### 必要なプロバイダーを宣言する
<a name="required-providers"></a>

プロバイダー設定はモジュール間で共有されますが、共有モジュールは独自の[プロバイダー要件](https://developer.hashicorp.com/terraform/language/providers/requirements)も宣言する必要があります。この方法により、Terraform は、設定内のすべてのモジュールと互換性のあるプロバイダーの単一バージョンがあることを確認し、プロバイダーのグローバル (モジュールに依存しない) 識別子として機能するソースアドレスを指定できます。ただし、モジュール固有のプロバイダー要件では、 など、プロバイダーがどのリモートエンドポイントにアクセスするかを決定する設定は指定されません AWS リージョン。

バージョン要件を宣言し、ハードコードされたプロバイダー設定を回避することで、モジュールは共有プロバイダーを使用して Terraform 設定間で移植性と再利用性を提供します。

共有モジュールの場合、 の [required\$1providers ブロック](https://developer.hashicorp.com/terraform/language/modules/develop/providers#provider-version-constraints-in-modules)で最低限必要なプロバイダーバージョンを定義します`versions.tf`。

モジュールが特定のバージョンの AWS プロバイダーを必要とすることを宣言するには、 `required_providers`ブロック内で `terraform`ブロックを使用します。

```
terraform {
  required_version = ">= 1.0.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 4.0.0"
    }
  }
}
```

共有モジュールが特定のバージョンの AWS プロバイダーのみをサポートしている場合は、*悲観的制約演算子* (`~>`) を使用します。これにより、右端のバージョンコンポーネントのみが増分されます。

```
terraform {
  required_version = ">= 1.0.0"

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

この例では、 は `4.57.1`と のインストール`~> 4.0`を許可します`4.67.0`が、 のインストールは許可しません`5.0.0`。詳細については、HashiCorp ドキュメントの[「バージョン制約構文](https://developer.hashicorp.com/terraform/language/expressions/version-constraints#version-constraint-syntax)」を参照してください。

## 命名規則に従う
<a name="naming-conventions"></a>

わかりやすいわかりやすい名前を使用すると、モジュール内のリソースと設定値の目的との関係を簡単に理解できます。スタイルガイドラインとの整合性により、モジュールユーザーとメンテナーの両方の読みやすさが向上します。

### リソースの命名に関するガイドラインに従う
<a name="resource-naming"></a>
+ Terraform スタイルの標準に一致するように、すべてのリソース名に *snake\$1case* (小文字はアンダースコアで区切られます) を使用します。この方法により、リソースタイプ、データソースタイプ、およびその他の事前定義された値の命名規則との整合性が確保されます。この規則は [name 引数](https://www.terraform.io/docs/glossary#argument)には適用されません。
+ そのタイプの唯一のリソース (モジュール全体の 1 つのロードバランサーなど) への参照を簡素化するには、リソースに `main`または という名前を`this`付けます。
+ リソースの目的とコンテキストを記述し、同様のリソース (たとえば、メインデータベース`primary`の場合は 、データベースのリードレプリカ`read_replica`の場合は ) を区別するのに役立つ意味のある名前を使用します。
+ 複数名ではなく単数名を使用します。
+ リソース名でリソースタイプを繰り返しないでください。

### 変数の命名に関するガイドラインに従う
<a name="variable-naming"></a>
+ ディスクサイズや RAM サイズ (ギガバイト単位の `ram_size_gb` RAM サイズなど) などの数値を表す入力、ローカル変数、出力の名前に単位を追加します。この方法により、設定メンテナーに対して期待される入力単位が明確になります。
+ ストレージサイズには MiB や GiB などのバイナリ単位を使用し、他のメトリクスには MB や GB などの小数単位を使用します。
+ などのブール変数に正の名前を付けます`enable_external_access`。

## アタッチメントリソースを使用する
<a name="attachment-resources"></a>

一部のリソースには、擬似リソースが属性として埋め込まれています。可能であれば、これらの埋め込みリソース属性の使用を避け、代わりに一意のリソースを使用してその擬似リソースをアタッチする必要があります。これらのリソース関係は、リソースごとに固有のcause-and-effectの問題を引き起こす可能性があります。

埋め込み属性の使用 (このパターンは避けてください）。

```
resource "aws_security_group" "allow_tls" {
  ...
  ingress {
    description      = "TLS from VPC"
    from_port        = 443
    to_port          = 443
    protocol         = "tcp"
    cidr_blocks      = [aws_vpc.main.cidr_block]
    ipv6_cidr_blocks = [aws_vpc.main.ipv6_cidr_block]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}
```

アタッチメントリソースの使用 (推奨):

```
resource "aws_security_group" "allow_tls" {
  ...
}

resource "aws_security_group_rule" "example" {
  type              = "ingress"
  description      = "TLS from VPC"
  from_port        = 443
  to_port          = 443
  protocol         = "tcp"
  cidr_blocks      = [aws_vpc.main.cidr_block]
  ipv6_cidr_blocks = [aws_vpc.main.ipv6_cidr_block]
  security_group_id = aws_security_group.allow_tls.id
}
```

## デフォルトのタグを使用する
<a name="default-tags"></a>

タグを受け入れることができるすべてのリソースにタグを割り当てます。Terraform AWS プロバイダーには、ルートモジュール内で使用する必要がある [aws\$1default\$1tags](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/default_tags) データソースがあります。

Terraform モジュールによって作成されたすべてのリソースに必要なタグを追加することを検討してください。アタッチできるタグのリストを次に示します。
+ **名前**: 人間が読めるリソース名
+ **AppId**: リソースを使用するアプリケーションの ID
+ **AppRole**: リソースの技術機能。たとえば、「webserver」や「database」など。
+ **AppPurpose**: リソースのビジネス目的。例：「フロントエンド ui」または「支払いプロセッサ」
+ **環境**: 開発、テスト、製品などのソフトウェア環境
+ **プロジェクト**: リソースを使用するプロジェクト
+ **CostCenter**: リソース使用量の請求対象

## Terraform レジストリ要件を満たす
<a name="registry-reqs"></a>

モジュールリポジトリは、Terraform レジストリに発行できるように、以下のすべての要件を満たしている必要があります。

短期的にモジュールをレジストリに公開する予定がない場合でも、常にこれらの要件に従う必要があります。これにより、リポジトリの設定と構造を変更することなく、後でモジュールをレジストリに発行できます。
+ リポジトリ名: モジュールリポジトリには、3 つの部分からなる名前 を使用します。 は`terraform-aws-<NAME>`、モジュールが管理するインフラストラクチャのタイプ`<NAME>`を反映します。`<NAME>` セグメントには、追加のハイフン ( など`terraform-aws-iam-terraform-roles`) を含めることができます。
+ 標準モジュール構造: モジュールは標準リポジトリ構造に従う必要があります。これにより、レジストリはモジュールを検査し、ドキュメントを生成したり、リソースの使用状況を追跡したりできます。
  + Git リポジトリを作成したら、モジュールファイルをリポジトリのルートにコピーします。再利用可能な各モジュールを独自のリポジトリのルートに配置することをお勧めしますが、サブディレクトリからモジュールを参照することもできます。
  + HCP Terraform を使用している場合は、組織レジストリと共有することを意図したモジュールを発行します。レジストリは、HCP Terraform API トークンを使用してダウンロードを処理してアクセスを制御するため、コンシューマーはコマンドラインから Terraform を実行してもモジュールのソースリポジトリにアクセスする必要はありません。
+ 場所とアクセス許可: リポジトリは、設定された[バージョン管理システム (VCS) プロバイダー](https://developer.hashicorp.com/terraform/cloud-docs/vcs)のいずれかに存在し、HCP Terraform VCS ユーザーアカウントにはリポジトリへの管理者アクセス権が必要です。レジストリには、新しいモジュールバージョンをインポートするためのウェブフックを作成するための管理者アクセスが必要です。
+ リリースの x.y.z タグ: モジュールを発行するには、少なくとも 1 つのリリースタグが必要です。レジストリは、リリースタグを使用してモジュールバージョンを識別します。リリースタグ名にはセマンティックバージョニングを使用する必要があります。セ[マンティックバージョニング](https://semver.org/)には、オプションで のプレフィックスを付けることができます `v` (例: `v1.1.0`および `1.1.0`)。レジストリは、バージョン番号に似ていないタグを無視します。モジュールの公開の詳細については、[Terraform ドキュメント](https://developer.hashicorp.com/terraform/cloud-docs/registry/publish-modules#publishing-a-new-module)を参照してください。

詳細については、Terraform [ドキュメントの「モジュールリポジトリの準備](https://developer.hashicorp.com/terraform/cloud-docs/registry/publish-modules#preparing-a-module-repository)」を参照してください。

## 推奨されるモジュールソースを使用する
<a name="module-sources"></a>

Terraform は、モジュールブロックの `source`引数を使用して、子モジュールのソースコードを検索およびダウンロードします。

繰り返しコード要素を除外することを主な目的とする密接に関連するモジュールにはローカルパスを使用し、複数の設定で共有することが意図されているモジュールにはネイティブの Terraform モジュールレジストリまたは VCS プロバイダーを使用することをお勧めします。

次の例は、モジュールを共有するための最も一般的なソースタイプと推奨される[ソースタイプ](https://developer.hashicorp.com/terraform/language/modules/sources)を示しています。レジストリモジュールは[バージョニング](https://developer.hashicorp.com/terraform/language/modules/syntax#version)をサポートしています。次の例に示すように、常に特定のバージョンを指定する必要があります。

### レジストリ
<a name="registry-examples"></a>

*Terraform レジストリ:*

```
module "lambda" {
  source = "github.com/terraform-aws-modules/terraform-aws-lambda.git?ref=e78cdf1f82944897ca6e30d6489f43cf24539374" #--> v4.18.0

  ...

}
```

コミットハッシュを固定することで、サプライチェーン攻撃に対して脆弱なパブリックレジストリからのドリフトを回避できます。

*HCP Terraform:*

```
module "eks_karpenter" {
  source = "app.terraform.io/my-org/eks/aws"
  version = "1.1.0"

  ...

  enable_karpenter = true
}
```

*Terraform Enterprise:*

```
module "eks_karpenter" {
  source = "terraform.mydomain.com/my-org/eks/aws"
  version = "1.1.0"

  ...

  enable_karpenter = true
}
```

### VCS プロバイダー
<a name="vcs-examples"></a>

VCS プロバイダーは、次の例に示すように、特定のリビジョンを選択するための `ref`引数をサポートしています。

*GitHub (HTTPS):*

```
module "eks_karpenter" {
  source = "github.com/my-org/terraform-aws-eks.git?ref=v1.1.0"

  ...

  enable_karpenter = true
}
```

*汎用 Git リポジトリ (HTTPS):*

```
module "eks_karpenter" {
  source = "git::https://example.com/terraform-aws-eks.git?ref=v1.1.0"

  ...

  enable_karpenter = true
}
```

*汎用 Git リポジトリ (SSH):*

**警告**  
プライベートリポジトリにアクセスするには、認証情報を設定する必要があります。

```
module "eks_karpenter" {
  source = "git::ssh://username@example.com/terraform-aws-eks.git?ref=v1.1.0"

  ...

  enable_karpenter = true
}
```

## コーディング標準に従う
<a name="coding-standards"></a>

すべての設定ファイルに一貫した Terraform フォーマットルールとスタイルを適用します。CI/CD パイプラインで自動スタイルチェックを使用して標準を適用します。コーディングのベストプラクティスをチームワークフローに埋め込むと、組織全体で使用が広く普及するにつれて、設定は読みやすく、保守可能で、共同作業性が維持されます。

### スタイルガイドラインに従う
<a name="style-guidelines"></a>
+ HashiCorp スタイルの標準に一致するように、すべての Terraform ファイル (`.tf` ファイル) [を terraform fmt](https://developer.hashicorp.com/terraform/cli/commands/fmt) コマンドでフォーマットします。
+ [terraform validate](https://developer.hashicorp.com/terraform/cli/commands/validate) コマンドを使用して、設定の構文と構造を検証します。
+ [TFLint](https://github.com/terraform-linters/tflint) を使用してコード品質を静的に分析します。この linter は、フォーマットだけでなく、Terraform のベストプラクティスをチェックし、エラーが発生したときにビルドに失敗します。

### 事前コミットフックを設定する
<a name="hooks"></a>

コミットを許可する前に`terraform fmt`、、`tflint``checkov`、、およびその他のコードスキャンとスタイルチェックを実行するクライアント側の事前コミットフックを設定します。このプラクティスは、開発者ワークフローの早い段階で標準への準拠を検証するのに役立ちます。

事前コミットなどの[事前コミット](https://pre-commit.com/)フレームワークを使用して、Terraform リンティング、フォーマット、コードスキャンをローカルマシンのフックとして追加します。フックは各 Git コミットで実行され、チェックに合格しなかった場合はコミットに失敗します。

スタイルチェックと品質チェックをローカルのコミット前フックに移動すると、変更が導入される前に開発者に迅速なフィードバックが提供されます。標準はコーディングワークフローの一部になります。