Gitのサブモジュール機能とTerraformのモジュール機能で実現するCI/CDパイプラインの量産(Lambda編)

# はじめに
Terraformのモジュール機能は最初はありがたみを感じられなかったので使わなかったものの、CI/CDパイプラインをいろいろと量産するようになってくると、いちいち過去のtfファイルを流用しようとして「どれが最新だったっけ……」と悩むようになったので、そろそろリポジトリ管理をするべきなのでは、と考えていた。

そんな折、Gitのサブモジュール機能を知って「これってもしかして組み合わせれば「おれの考えた最強量産型CI/CDパイプラインができるのでは？」と思い、作ってみた。

# 全体構成
今回は以下のような構成だ。

```メインモジュール
terraform-mainmodule-lambdapipeline
├── .gitmodules
├── main.tf
└── modules
    ├── 01_variables.tf
    ├── 02_iam.tf
    ├── 03_s3.tf
    ├── 04_cloudwatchlogs.tf
    ├── 05_codepipeline.tf
    └── 06_cloudformation_parameter.json
```

modules配下を、以下のようにサブモジュールとして登録している。

```.gitmodules
[submodule "modules"]
        path = modules
        url = https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/terraform-submodule-lambdapipeline
```

サブモジュールの構成は、↑の通りではあるが、ちゃんと書くと以下のようになる。

```サブモジュール
terraform-submodule-lambdapipeline
├── 01_variables.tf
├── 02_iam.tf
├── 03_s3.tf
├── 04_cloudwatchlogs.tf
├── 05_codepipeline.tf
└── 06_cloudformation_parameter.json
```

# ポイント
ポイントといっても、あまり大したことはない。

メインモジュール側には、以下のように```provider```と```module```の定義をしている。
量産するときに変更したい項目を、moduleの引数として定義しているくらいだ。

```HCL:main.tf
######################################################################
# Provider                                                           #
######################################################################
provider "aws" {
  region = "ap-northeast-1"
}

######################################################################
# Variables                                                          #
######################################################################
variable "prefix" {
  default = "LambdaPipeline"
}

######################################################################
# Module                                                             #
######################################################################
module "lambda_pipeline" {
  source = "./modules"

  ####################################################################
  # IAM                                                              #
  ####################################################################
  codebuild_role_name      = "${var.prefix}-CodeBuildRole"
  codepipeline_role_name   = "${var.prefix}-CodePipelineRole"
  cloudformation_role_name = "${var.prefix}-CloudFormationRole"

  ####################################################################
  # S3 Bucket for CodePipeline Artifact                              #
  ####################################################################
  bucket_name = lower("${var.prefix}-artifact-bucket")

  ####################################################################
  # CloudWatch Logs                                                  #
  ####################################################################
  codebuild_logstream_name = "${var.prefix}-CodeBuildLogStream"

  ####################################################################
  # CodeCommit                                                       #
  ####################################################################
  repository_name = lower("${var.prefix}")

  ####################################################################
  # CodeBuild                                                        #
  ####################################################################
  buildspec_file_name = "buildspec.yml"
  build_project_name = "${var.prefix}-BuildProject"

  ####################################################################
  # CodePipeline                                                     #
  ####################################################################
  pipeline_name = "${var.prefix}-Pipeline"

  ####################################################################
  # SAM                                                              #
  ####################################################################
  stack_name                           = "${var.prefix}-SAMStack"
  cf_param_lambda_function_name        = "${var.prefix}-Function"
  cf_param_lambda_execution_role_name  = "${var.prefix}-LambdaExecutionRole"
}
```

また、moduleで使う変数は、サブモジュール側でも変数宣言しておく必要があるため、01_variables.tfは以下のように書いておく。区分をmain.tfと合わせておくと分かりやすいだろう。

```HCL:01_variables.tf
######################################################################
# IAM                                                                #
######################################################################
variable "codebuild_role_name"      {}
variable "codepipeline_role_name"   {}
variable "cloudformation_role_name" {}

######################################################################
# S3 Bucket for CodePipeline Artifact                                #
######################################################################
variable "bucket_name" {}

######################################################################
# CloudWatch Logs                                                    #
######################################################################
variable "codebuild_logstream_name" {}

######################################################################
# CodeCommit                                                         #
######################################################################
variable "repository_name" {}

######################################################################
# CodeBuild                                                          #
######################################################################
variable "buildspec_file_name" {}
variable "build_project_name"  {}

######################################################################
# CodePipeline                                                       #
######################################################################
variable "pipeline_name" {}

######################################################################
# SAM                                                                #
######################################################################
variable "stack_name"                          {}
variable "cf_param_lambda_function_name"       {}
variable "cf_param_lambda_execution_role_name" {}
```

# パイプラインの内容
ここはこれまでも何度か書いてきたパイプラインなので、あまり改めて語るところはない。
[過去の記事](https://qiita.com/neruneruo/items/cda42244ad8e1e2d83b0#terraform%E3%81%AE%E5%AE%9A%E7%BE%A9)とほぼ変わらない内容である。
「ほぼ」と書いたのは、過去の記事では変数をlocalsで作っていたが、今回はモジュール化している都合上variablesにする必要があるため、すべて```${local.hogehoge}```という記述から```${var.hogehoge}```に変更したくらいだ。

# 作ったモジュールのメンテナンスについて
さて、これでパイプラインはモジュール化できて好きなように量産可能になったが、パイプライン単位にカスタマイズ要素が発生したらどうしたら良いのだろう？

サブモジュール側に、パイプラインの特性に合わせた条件分岐やマップを追加していくと、このサブモジュール自体がモノリシックに育っていってしまう可能性があるだろう。
しかし、だからといってカスタマイズする都度、サブモジュールからdetachして個別のモジュールとしてメイン側に取り込んでいってしまうと、今後このサブモジュールをバージョンアップするときに対象から外れてしまう。
AWSでモノを作る以上、ずっと同じ型を使い続けるというのはナンセンスだろう。

モジュール化のプラクティスは理解できたが、これをどうやって見通し良くメンテナンスしていくかは答えがでていない……。