我尝试将Terraform计划结果的说明功能集成到CI/CD中,请ChatGPT帮我解释!

背景:

基础设施工程师:“请帮我审查一下这段Terraform代码的PR。”
基础设施工程师:“请查看变更点的plan结果↓。”

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
  ~ update in-place
  - destroy
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.example1 will be created
  + resource "aws_instance" "example1" {
      + ami                          = "ami-abc123"
      + instance_type                = "t2.micro"
      + key_name                     = "example_key"
      + vpc_security_group_ids       = (known after apply)
      + subnet_id                    = "subnet-123456"
      + associate_public_ip_address  = true
      + tags = {
          + "Name" = "example1"
        }
    }

  # aws_instance.example2 will be updated in-place
  ~ resource "aws_instance" "example2" {
      ~ instance_type                = "t2.micro" -> "t2.small"
        ami                          = "ami-abc123"
        key_name                     = "example_key"
        vpc_security_group_ids       = "sg-abc123"
        subnet_id                    = "subnet-123456"
        associate_public_ip_address  = true
        tags = {
            "Name" = "example2"
        }
    }

  # aws_instance.example3 will be destroyed
  - resource "aws_instance" "example3" {
      - ami                          = "ami-abc123" -> null
      - instance_type                = "t2.micro" -> null
      - key_name                     = "example_key" -> null
      - vpc_security_group_ids       = "sg-abc123" -> null
      - subnet_id                    = "subnet-123456" -> null
      - associate_public_ip_address  = true -> null
      - tags = {
            "Name" = "example3"
        } -> null
    }

  # aws_instance.example4 must be replaced
-/+ resource "aws_instance" "example4" {
      ~ ami                          = "ami-abc123" -> "ami-def456" # forces replacement
      ~ instance_type                = "t2.micro" -> "t2.small"
        key_name                     = "example_key"
        vpc_security_group_ids       = "sg-abc123"
        subnet_id                    = "subnet-123456"
        associate_public_ip_address  = true
        tags = {
            "Name" = "example4"
        }
    }

Plan: 2 to add, 1 to change, 2 to destroy.

应该看哪里呢…

当团队成员要求对Terraform代码进行审核时,我认为会发生类似上述对话的情况,但这种审核对于初学者来说却更难一些。
具体而言,我觉得很难在查看像上面的计划结果时,快速理解以下几个方面。

    • この変更によってどのような影響があるのか

 

    この変更で気を付けるべき点はどこか

因此,我尝试创建一个功能,让ChatGPT(gpt-3.5或gpt-4)解释并展示计划的结果。

示例

在特定的分支上,我们对Terraform文件进行了如下修改。
将这个差异提交并推送到远程仓库中。

resource "aws_codecommit_repository" "sample_1" {
  repository_name = "sample-1"
}
- resource "aws_codecommit_repository" "sample_2" {
-   repository_name = "sample_2"
- }

+ resource "aws_codecommit_repository" "sample_qiita_article" {
+   repository_name = "sample-qiita-article"
+   description = "This repository is for the article of Qiita."
+   default_branch = "mastar"
+ }

然后,CodePipeline会像图片一样启动。

codepipeline_plan.png

按下「詳細」按钮后,您可以在下方确认GPT为您提供的日志说明。

> Entering new  chain...
Prompt after formatting:
System: 
            あなたはterraformのplan結果を解釈し、噛み砕いて初心者にもわかりやすく説明することができます。
            下記のplan結果を読み解いて、どのような変更が加わるか、どのような点に注意すべきかを500文字程度で説明してください。
        
Human: 
            ```:plan結果
            
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
  - destroy

Terraform will perform the following actions:

  # aws_codecommit_repository.sample_2 will be destroyed
  - resource "aws_codecommit_repository" "sample_2" {
      - arn             = "arn:aws:codecommit:ap-northeast-1:any_account_id:sample-2" -> null
      - clone_url_http  = "https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample-2" -> null
      - clone_url_ssh   = "ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample-2" -> null
      - id              = "sample-2" -> null
      - repository_id   = "sample_repository_id" -> null
      - repository_name = "sample-2" -> null
      - tags            = {} -> null
      - tags_all        = {} -> null
    }

  # aws_codecommit_repository.sample_qiita_article will be created
  + resource "aws_codecommit_repository" "sample_qiita_article" {
      + arn             = (known after apply)
      + clone_url_http  = (known after apply)
      + clone_url_ssh   = (known after apply)
      + default_branch  = "master"
      + description     = "This repository is for the article of Qiita."
      + id              = (known after apply)
      + repository_id   = (known after apply)
      + repository_name = "sample-qiita-article"
      + tags_all        = (known after apply)
    }

Plan: 1 to add, 0 to change, 1 to destroy.

            ```
        

> Finished chain.
ChatGPTからひとこと:
このplan結果では、Terraformが以下の変更を加えることを示しています。

1. `aws_codecommit_repository.sample_2`というリポジトリが削除されます。
   - 削除されるリポジトリの詳細情報(ARN、clone URLなど)が表示されています。
   - このリポジトリに関連するリソースはすべて削除されます。

2. `aws_codecommit_repository.sample_qiita_article`というリポジトリが作成されます。
   - このリポジトリにはまだ詳細情報がわからないため、apply後にARNやclone URLなどが表示されます。
   - リポジトリのデフォルトブランチは"master"に設定され、Qiitaの記事用のリポジトリであることが説明されています。

プランの結果は、1つのリポジトリが削除され、もう1つのリポジトリが作成されることを示しています。変更を適用する前に、これらの変更が意図したものであることを確認してください。また、削除されるリポジトリに関連するリソースがある場合は注意が必要です。

机制具体细节

ManyDesign (2).png

在GitHub或CodeCommit等平台上推送Terraform文件的更改时,AWS CodeBuild将被触发,执行terraform plan指令并将结果输入给ChatGPT,然后展示响应信息。

这样一来,例如在公关的证据中,您可以直接复制粘贴计划结果而无需自己撰写解释,只需将CodeBuild的日志粘贴进去就可以了。

实施示例

层级图

.
└── main.tf/
    └── gpt_plan/
        ├── buildspec_plan.yml
        ├── explain_plan_result.py
        └── install_terraform.sh

CodeBuild实现部分

resource "aws_codebuild_project" "terraform_plan" {
  name          = "gpt_plan_build_sample"
  service_role  = your_codebuild_role
  build_timeout = "5" # minutes

  artifacts {
    type = "NO_ARTIFACTS"
  }
  environment {
    compute_type                = "BUILD_GENERAL1_SMALL"
    image                       = "aws/codebuild/standard:5.0"
    type                        = "LINUX_CONTAINER"
    image_pull_credentials_type = "CODEBUILD"
  }
  # CodePipelineから実行する場合にはCodePipelineのconfigurationが優先される
  source {
    type            = "CODECOMMIT" # or GITGUB, S3, etc.
    location        = "https://git-codecommit.ap-northeast-1.amazonaws.com/your_repository_location"
    git_clone_depth = 1
    buildspec       = "gpt_plan/buildspec_plan.yml"
  }
}
version: 0.2

env:
  parameter-store:
    OPENAI_API_KEY: "openai_api_key"

phases:
  install:
    runtime-versions:
      python: 3.8
    commands:
      - chmod +x gpt_plan/install_terraform.sh
      - ./gpt_plan/install_terraform.sh
  build:
    commands:
      - terraform init
      - terraform plan -out=plan_result.txt
  post_build:
    on-failure: CONTINUE
    commands:
      # ANSIエスケープコードと呼ばれる、SHELL上で文字色等を装飾するためのコードをsedで削除してからplan_result_fmt.txtに出力
      - terraform show plan_result.txt | sed 's/\x1b\[[0-9;]*m//g' > plan_result_fmt.txt
      - pip install langchain
      - pip install openai
      - python -m gpt_plan.explain_plan_result plan_result_fmt.txt
#!/bin/bash
apt-get update
apt-get install -y unzip
curl -o /tmp/terraform.zip -LO https://releases.hashicorp.com/terraform/0.14.5/terraform_0.14.5_linux_amd64.zip # versionはお好みで指定
unzip /tmp/terraform.zip -d /usr/local/bin/
terraform --version

代码管道的实现部分

resource "aws_codepipeline" "gpt_plan_pipeline_sample" {
    name     = "gpt_plan_pipeline_sample"
    role_arn = your_codepipeline_role

    artifact_store {
        location = aws_s3_bucket.artifact_store.bucket
        type     = "S3"
    }

    stage {
        name = "Source"

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

            # codebuildのsourceよりもこちらが優先される
            configuration = {
                RepositoryName = your_repository_name # tf差分ファイルのpush先リポジトリを指定する
                BranchName     = "develop" # or master or main
            }
        }
    }

    stage {
        name = "BuildAndPlan"

        action {
            name             = "Plan"
            category         = "Build"
            owner            = "AWS"
            provider         = "CodeBuild"
            input_artifacts  = ["source_output"]
            version          = "1"

            configuration = {
                ProjectName = aws_codebuild_project.gpt_plan_build_sample.name
            }
        }
    }
}

Python ChatGPT输入部分

import sys

from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain

from langchain.prompts.chat import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)

if len(sys.argv) < 2:
    print("Usage: python explain_plan_result.py <plan_file>")
    sys.exit(1)


plan_file = sys.argv[1]


class Prompt:
    def pmt_tmpl(self) -> ChatPromptTemplate:
        sys_template = """
            あなたはterraformのplan結果を解釈し、噛み砕いて初心者にもわかりやすく説明することができます。
            下記のplan結果を読み解いて、どのような変更が加わるか、どのような点に注意すべきかを{summary_words}文字程度で説明してください。
        """
        sys_pmt = SystemMessagePromptTemplate.from_template(sys_template)
        human_template = """
            ```:plan結果
            {plan_result}
            ```
        """
        human_pmt = HumanMessagePromptTemplate.from_template(human_template)
        chat_pmt = ChatPromptTemplate.from_messages([sys_pmt, human_pmt])
        return chat_pmt
    
    def vairables(self, summary_words: int, plan_result: str) -> dict:
        return {
            "summary_words": summary_words,
            "plan_result": plan_result,
        }

def get_plan_result() -> str:
    with open(plan_file, "r") as f:
        plan_result: str = f.read()
    return plan_result
    

def explain_plan_result() -> None:
    llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.5)
    pmt_cls = Prompt()
    prompt = pmt_cls.pmt_tmpl()
    chain = LLMChain(llm=llm, prompt=prompt, verbose=True)
    chain_input = pmt_cls.vairables(summary_words=500, plan_result=get_plan_result())
    llm_resp = chain.run(chain_input)
    print(f"ChatGPTからひとこと:\n{llm_resp}")


if __name__ == "__main__":
    explain_plan_result()

系统案例的深入挖掘

在提交Terraform代码的PR时,也尝试附上以下的说明文。在以下情况下可能会有用。

    • Terraformに明るくないレビュアーがいる場合

レビュアーがTerraformに精通していなくても、この機能により変更点を理解する手助けができます。

他部署の人もレビューに参加していて、変更の要点を簡潔に説明したい場合

変更点を要約することで、プロジェクト全体を円滑に進めることができます。

专栏:自定义ChatGPT的命令语句
通过改进输入作为LangChain系统模板(sys_template = ~)的文章,我们可以让ChatGPT提供更加针对特定用途的解释。
例如,我想到可以通过指定sys_template = “您可以从terraform的plan结果中准确地检测到拼写错误。~”来将其命名为”拼写检测小助手”。

想法

我在我所参与的项目中尝试了一下。
虽然对于简单的计划结果来说,并没有太高的实用性,但对于复杂的计划结果,它可能会有助于理解。
此外,在进行审核时,我也对ChatGPT会说些什么感到有些期待,这也是一个好处。(个人差异存在)

在继续进行细微改进的同时,我们期待着未来它将如何为我们提供帮助。

广告
将在 10 秒后关闭
bannerAds