terraform-docs로 README.md 자동 작성하는 방법

박종배·2023년 4월 12일
0
post-thumbnail

참고


개요

terraform module을 만들었다면 README.md를 잘 작성해줘야 함. 그런데 module이 좀 복잡해지면 잘못된 내용을 담을 수 있거나 매 변경 때마다 문서 내용을 업데이트하는 것은 쉬운게 아님. 그래서 .tf 파일 경로만 지정해주면 다 읽어서 문서를 만들어주는 terraform-docs를 적용해보자. 그리고 이 적용 또한 commit할 때마다 자동으로 수행되도록 pre-commit hook도 사용해보자.

terraform-docs 란?

terraform module로부터 다양한 포맷으로 도큐먼트 생성해주는 유틸리티.

설치

$ brew install terraform-docs

### 확인
$ terraform-docs                                                                                  
A utility to generate documentation from Terraform modules in various output formats

Usage:
  terraform-docs [PATH] [flags]
  terraform-docs [command]

Available Commands:
  asciidoc    Generate AsciiDoc of inputs and outputs
  completion  Generate shell completion code for the specified shell (bash or zsh)
  help        Help about any command
  json        Generate JSON of inputs and outputs
  markdown    Generate Markdown of inputs and outputs
  pretty      Generate colorized pretty of inputs and outputs
  tfvars      Generate terraform.tfvars of inputs
  toml        Generate TOML of inputs and outputs
  version     Print the version number of terraform-docs
  xml         Generate XML of inputs and outputs
  yaml        Generate YAML of inputs and outputs

Flags:
  -c, --config string               config file name (default ".terraform-docs.yml")
      --footer-from string          relative path of a file to read footer from (default "")
      --header-from string          relative path of a file to read header from (default "main.tf")
  -h, --help                        help for terraform-docs
      --hide strings                hide section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
      --lockfile                    read .terraform.lock.hcl if exist (default true)
      --output-check                check if content of output file is up to date (default false)
      --output-file string          file path to insert output into (default "")
      --output-mode string          output to file method [inject, replace] (default "inject")
      --output-template string      output template (default "<!-- BEGIN_TF_DOCS -->\n{{ .Content }}\n<!-- END_TF_DOCS -->")
      --output-values               inject output values into outputs (default false)
      --output-values-from string   inject output values from file into outputs (default "")
      --read-comments               use comments as description when description is empty (default true)
      --recursive                   update submodules recursively (default false)
      --recursive-path string       submodules path to recursively update (default "modules")
      --show strings                show section [all, data-sources, footer, header, inputs, modules, outputs, providers, requirements, resources]
      --sort                        sort items (default true)
      --sort-by string              sort items by criteria [name, required, type] (default "name")
  -v, --version                     version for terraform-docs

Use "terraform-docs [command] --help" for more information about a command.

기본 사용법

$ terraform-docs markdown table \
    --output-file README.md \
    --output-mode inject \
    .
  • 이렇게 하면 현재 경로에 있는 terraform 파일들을 읽어 README.md로 만들어주는데 output-mode가 inject라서 사용자가 작성한 부분 밑에 <!-- BEGIN_TF_DOCS --><!-- END_TF_DOCS --> 사이로 추가되므로 사용자가 추가하고 싶은 내용이 있다면 그 위나 아래에 작성하자.

샘플 README.md

## 용도

- docker image build하는 CI나 k8s에 워크로드를 띄우는 CD 작업을 CodeSeries로 할 때 활용.

## 효과

- 최소 권한 법칙에 따라 IAM을 CI/CD 단위로 생성하고 권한을 최소화 함.
- 매번 CodeBuild, CodePipeline, IAM, S3를 만들 필요 없이 이 모듈을 사용하면 한 번에 만들어주며 추적 관리가 가능함.

## 범위

가능

- CI나 CD를 뺄 수 있음.
- 기본적으로 CD에는 approval을 넣으려 하는데 뺼 수 있음.
- buildspec path 수정.
- CodeCommit 소스 엔드포인트/브랜치 수정.
- 접근할 ECR ARN 수정.
- attach할 IAM policy ARN 수정.
- tags 추가.

불가능

- k8s에 apply하는 RBAC는 직접 적용해줘야 함(권고 사항)
- Source는 반드시 CodeCommit 이어야 함
- Source (CodeCommit) -> Build (CodeBuild) -> Deploy on EKS (CodeBuild) 구조가 아니라면 사용 불가.

## 사용 방법

기본 main.tf 작성 방법 (`env/prod/cicd/test`)

``` bash
## 용도

- docker image build하는 CI나 k8s에 워크로드를 띄우는 CD 작업을 CodeSeries로 할 때 활용.

## 효과

- 최소 권한 법칙에 따라 IAM을 CI/CD 단위로 생성하고 권한을 최소화 함.
- 매번 CodeBuild, CodePipeline, IAM, S3를 만들 필요 없이 이 모듈을 사용하면 한 번에 만들어주며 추적 관리가 가능함.

## 범위

가능

- CI나 CD를 뺄 수 있음.
- 기본적으로 CD에는 approval을 넣으려 하는데 뺼 수 있음.
- buildspec path 수정.
- CodeCommit 소스 엔드포인트/브랜치 수정.
- 접근할 ECR ARN 수정.
- attach할 IAM policy ARN 수정.
- tags 추가.

불가능

- k8s에 apply하는 RBAC는 직접 적용해줘야 함(권고 사항)
- Source는 반드시 CodeCommit 이어야 함
- Source (CodeCommit) -> Build (CodeBuild) -> Deploy on EKS (CodeBuild) 구조가 아니라면 사용 불가.

## 사용 방법

기본 main.tf 작성 방법 (`env/prod/cicd/test`)

``` bash
terraform {  ### optional
  backend "s3" {
    bucket  = "<your value>"
    key     = "<your value>/terraform.tfstate"
    region  = "<your value>"
    encrypt = true
  }
}

module "cicd-with-eks" {
  source = "../../../../modules/cicd-with-eks/"  ### relative

  name_custom_suffix = "test-set-your-name"

  tags_additional = {
    Purpose = "test-set-your-name"
  }

  this_iam_managed_policy_arns_additional = []

  this_ecr_arns = [
    "arn:aws:ecr:ap-northeast-2:set-your-aws-account-id:repository/set-your-repo",
    "arn:aws:ecr:ap-northeast-2:set-your-aws-account-id:repository/set-your-repo",
  ]

  codecommit_repo_name   = "set-your-codcommit-repo-name"
  codecommit_branch_name = "set-your-codcommit-branch-name"

  ci_buildspec_path = "buildspec.yml"

  ci_environment = {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/standard:6.0"
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
    privileged_mode             = true
  }

  cd_buildspec_path = "kubernetes/buildspec.yml"

  cd_environment = {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/standard:6.0"
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
    privileged_mode             = true
  }


  codepipeline_stages = [
    {
      name = "Source"

      actions = [
        {
          name             = "Source"
          category         = "Source"
          owner            = "AWS"
          provider         = "CodeCommit"
          version          = "1"
          input_artifacts  = [""]
          output_artifacts = ["source_output"]
          run_order        = 1

          ### will be added automatically from local block
          # configuration = {
          #   RepositoryName = "set-your-codcommit-repo-name"
          #   BranchName     = "set-your-codcommit-branch-name"
          # }
        }
      ]
    },
    {
      name = "Build"

      actions = [{
        name             = "Build"
        category         = "Build"
        owner            = "AWS"
        provider         = "CodeBuild"
        version          = "1"
        input_artifacts  = ["source_output"]
        output_artifacts = ["build_output"]
        run_order        = 1

        ### will be added automatically from local block
        # configuration = {
        #   ProjectName = "aws_codebuild_project.cicd_ci.name"
        # }
      }]
    },
    {
      name = "Delivery"

      actions = [
        {
          name             = "Approval"
          category         = "Approval"
          owner            = "AWS"
          provider         = "Manual"
          version          = 1
          run_order        = 1
          input_artifacts  = [""]
          output_artifacts = [""]

          ### will be added automatically from local block
          #   configuration {
          #     NotificationArn = "$var.approve_sns_arn}"
          #     CustomData = "${var.approve_comment}"
          #     ExternalEntityLink = "${var.approve_url}"
          #   
          # }
        },
        {
          name             = "Delivery"
          category         = "Build"
          owner            = "AWS"
          provider         = "CodeBuild"
          version          = "1"
          input_artifacts  = ["source_output"]
          output_artifacts = [""]
          run_order        = 2

          ### will be added automatically from local block
          # configuration = {
          #     ProjectName = aws_codebuild_project.cicd_cd.name
          # }
        }
      ]
    }
  ]
}

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3.7 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 4.47 |
| <a name="requirement_time"></a> [time](#requirement\_time) | ~> 0.9.1 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 4.60.0 |
| <a name="provider_null"></a> [null](#provider\_null) | 3.2.1 |
| <a name="provider_terraform"></a> [terraform](#provider\_terraform) | n/a |
| <a name="provider_time"></a> [time](#provider\_time) | 0.9.1 |

## Modules

No modules.

## Resources

| Name | Type |
|------|------|
| [aws_codebuild_project.this_cd](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource |
| [aws_codebuild_project.this_ci](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codebuild_project) | resource |
| [aws_codepipeline.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/codepipeline) | resource |
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_acl.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
| [null_resource.wait](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [time_static.this](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/static) | resource |
| [aws_codecommit_repository.this_codecommit](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/codecommit_repository) | data source |
| [aws_iam_policy_document.this_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.this_inline_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [terraform_remote_state.eks](https://registry.terraform.io/providers/hashicorp/terraform/latest/docs/data-sources/remote_state) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_cd_buildspec_path"></a> [cd\_buildspec\_path](#input\_cd\_buildspec\_path) | Path to the buildspec.yml for the CD stage | `string` | `"kubernetes/buildspec.yml"` | no |
| <a name="input_cd_environment"></a> [cd\_environment](#input\_cd\_environment) | CodeBuild environment for the CD stage | <pre>object({<br>    compute_type                = string<br>    image                       = string<br>    type                        = string<br>    image_pull_credentials_type = string<br>    privileged_mode             = bool<br>  })</pre> | <pre>{<br>  "compute_type": "BUILD_GENERAL1_SMALL",<br>  "image": "aws/codebuild/standard:6.0",<br>  "image_pull_credentials_type": "CODEBUILD",<br>  "privileged_mode": true,<br>  "type": "LINUX_CONTAINER"<br>}</pre> | no |
| <a name="input_ci_buildspec_path"></a> [ci\_buildspec\_path](#input\_ci\_buildspec\_path) | Path to the buildspec.yml for the CI stage | `string` | `"buildspec.yml"` | no |
| <a name="input_ci_environment"></a> [ci\_environment](#input\_ci\_environment) | CodeBuild environment for the CI stage | <pre>object({<br>    compute_type                = string<br>    image                       = string<br>    type                        = string<br>    image_pull_credentials_type = string<br>    privileged_mode             = bool<br>  })</pre> | <pre>{<br>  "compute_type": "BUILD_GENERAL1_SMALL",<br>  "image": "aws/codebuild/standard:6.0",<br>  "image_pull_credentials_type": "CODEBUILD",<br>  "privileged_mode": true,<br>  "type": "LINUX_CONTAINER"<br>}</pre> | no |
| <a name="input_codecommit_branch_name"></a> [codecommit\_branch\_name](#input\_codecommit\_branch\_name) | Branch name watched from the CodePipeline's source stage | `string` | `"set-your-codcommit-branch-name"` | no |
| <a name="input_codecommit_repo_name"></a> [codecommit\_repo\_name](#input\_codecommit\_repo\_name) | CodeCommit repository name used as a CodePipeline's source | `string` | `"set-your-codcommit-repo-name"` | no |
| <a name="input_codepipeline_stages"></a> [codepipeline\_stages](#input\_codepipeline\_stages) | CodePipeline definition for all stages | `any` | `[]` | no |
| <a name="input_create_cd"></a> [create\_cd](#input\_create\_cd) | Indicates whether or not to create a CodeBuild project for CD | `bool` | `true` | no |
| <a name="input_create_ci"></a> [create\_ci](#input\_create\_ci) | Indicates whether or not to create a CodeBuild project for CI | `bool` | `true` | no |
| <a name="input_name_custom_suffix"></a> [name\_custom\_suffix](#input\_name\_custom\_suffix) | suffix attached to name of all resources | `string` | `"set-your-name"` | no |
| <a name="input_tags_additional"></a> [tags\_additional](#input\_tags\_additional) | Additional tags that will be attached additionally to resources in this module | `map(string)` | `{}` | no |
| <a name="input_tags_default"></a> [tags\_default](#input\_tags\_default) | Default tags that will be attached to resources in this module | `map(string)` | <pre>{<br>  "Email": "set-your-email",<br>  "LocalTagTest": "true",<br>  "Owner": "set-your-id",<br>  "Purpose": "mlops",<br>  "Team": "datatech"<br>}</pre> | no |
| <a name="input_this_ecr_arns"></a> [this\_ecr\_arns](#input\_this\_ecr\_arns) | ECR ARNs to be allowed permissions | `list(string)` | <pre>[<br>  "arn:aws:ecr:ap-northeast-2:set-your-aws-account-id:repository/set-your-repo",<br>  "arn:aws:ecr:ap-northeast-2:set-your-aws-account-id:repository/set-your-repo"<br>]</pre> | no |
| <a name="input_this_iam_managed_policy_arns_additional"></a> [this\_iam\_managed\_policy\_arns\_additional](#input\_this\_iam\_managed\_policy\_arns\_additional) | Managed policies additionally attached | `list(string)` | `[]` | no |
| <a name="input_this_iam_managed_policy_arns_default"></a> [this\_iam\_managed\_policy\_arns\_default](#input\_this\_iam\_managed\_policy\_arns\_default) | Managed policies attached by default. | `list(string)` | <pre>[<br>  "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",<br>  "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",<br>  "arn:aws:iam::aws:policy/AmazonEKSServicePolicy",<br>  "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"<br>]</pre> | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_codebuild_cd_arn"></a> [codebuild\_cd\_arn](#output\_codebuild\_cd\_arn) | CodeBuild Cd project ARN |
| <a name="output_codebuild_ci_arn"></a> [codebuild\_ci\_arn](#output\_codebuild\_ci\_arn) | CodeBuild CI project ARN |
| <a name="output_codepipeline_arn"></a> [codepipeline\_arn](#output\_codepipeline\_arn) | CodePipeline ARN |
| <a name="output_role_arn"></a> [role\_arn](#output\_role\_arn) | Role ARN to be used by CodePipeline, CodeBuild |
| <a name="output_s3_bucket_arn"></a> [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | S3 Bucket ARN used by CodeSeries CICD |
<!-- END_TF_DOCS -->

위 예제에서 <!-- BEGIN_TF_DOCS -→ 이전에는 직접 작성한 내용이며 여기는 terraform-docs가 건드리지 않는 부분이므로 별도로 사용자가 추가하면 됨.

pre-commit hook 사용하기

매번 변경사항이 생길 때 마다 terraform-docs를 수동으로 실행하는 것은 귀찮고 휴먼 에러가 생길 여지가 있으므로 tf 소스 변경 후 커밋할 때 terraform-docs를 자동으로 수행하게 설정해보자.

소스코드 작업 후 push 전에 hook을 이용하여 사전 작업할 내용을 정의할 수 있는데 이를 활용해 변경된 terraform 코드에 대한 README.md를 terraform-docs로 자동 업데이트 해주자.

먼저, pre-commit 설치하기

### mac
$ brew install pre-commit

### python
$ pip install pre-commit

설치 확인

$ pre-commit --version

root 경로에 .pre-commit-config.yaml 생성

repos:
  - repo: https://github.com/terraform-docs/terraform-docs
    rev: "v0.16.0"
    hooks:
      - id: terraform-docs-go
        args: ["markdown", "table", "--output-file", "README.md", "--output-mode", "inject", "<path-to-tf>"]

활성화

테스트

  • 먼저, README.md를 업데이트해야 할 상황을 만들기 위해 tf 파일에 변화를 만들어주고 아래처럼 커밋했을 때 Failed가 나면서 설정한대로 terraform-docs 명령어 수행하여 README.md를 업데이트 해주고 stage에 올려주게 됨. 그리고 다시 커밋하면 Passed됨.
$ git commit -am "[update modules/cicd-wth-eks] pre-commit 테스트"
terraform-docs...........................................................Failed
- hook id: terraform-docs-go
- files were modified by this hook

modules/cicd-with-eks/README.md updated successfully

$ git commit -am "[update modules/cicd-wth-eks] pre-commit 테스트"
terraform-docs...........................................................Passed
[master a250c03] [update modules/cicd-wth-eks] pre-commit 테스트
 2 files changed, 4 insertions(+), 5 deletions(-)

주의 사항

  • vs code를 쓸 때, source conrtol UI로 하면 pre-commit hook이 작동하지 않음.
profile
기록하는 엔지니어 되기 💪

0개의 댓글