「Amazon CodeCatalyst × TaskCat × Amazon Q」で AWS CloudFormation テンプレートの快適な開発環境を作る

こんにちは。ひるたんぬです。

最近は気温差が激しく、体調を崩しやすいですね。。私もこのビッグウェーブにしっかりと乗り、体調を崩しておりました。
そんなあるとき、ふと気になることがありました。
「なぜ、ティッシュは必ず二枚重ねなのか。。」と。
駅前で配っているポケットティッシュから保湿成分配合の高級ティッシュまで、必ずティッシュは二枚一組¹になっています。
気になって調べたところ、

  • 紙には裏表があり、肌触りの良い面が必ず表面に来るようにするため
  • 重ねることで空気の層ができ、柔らかさ・吸水性が向上する

などといった理由があるそうです。身の回りの不思議がまた一つ解決してスッキリしました。
下記リンクを参考にしたのですが、私は小学5年生と同じ疑問を抱いていたようです。
参考:朝日小学生新聞「ティッシュペーパーはなぜ2枚重ね? 4枚重ねは「贈り物」にも」
¹ 今日では3枚・4枚と重ねている商品もあるようです。

…さて、今回は私が個人的な要件でAWSで開発を進めるにあたって、必ずと言っていいほどお世話になっているAmazon CodeCatalystを使って、AWS CloudFormationのテンプレートを快適に作成する環境を考えてみましたのでご紹介いたします。

このような記事の多くは、「CodePipeline」「CodeCommit」「CodeBuild」などのCodeファミリーを用いて作成されていることが多かったため、そのアイデアに着想を受け作成しております。
(CodeCommitについては、新規でAWSアカウントを開設した方は利用できなくなってしまったので…というのもあったりなかったり。。)
CodeCatalystで作成することで、Codeファミリーと同等の機能が実現できることはもちろん、Issue管理や開発環境の管理も一元的に行える点がメリットだと考えています。

概要

今回は、CodeCatalystを使用して以下を満たす環境を構築することができます。

  • CloudFormationテンプレートファイルのソースコード管理
    ➔ CodeCatalystの「Source repositories」を使用
  • CloudFormationテンプレートのコーディング(実装)環境
    ➔ CodeCatalystの「Dev Environments」を使用
  • CloudFormationテンプレートのテンプレートチェック
    ➔ CodeCatalystの「Workflows」を使用して各種テストを実行

上記の内最後の各種テストでは、yamlファイルの構文チェック・CloudFormationテンプレートの構文チェック・TaskCatを用いて実際にデプロイしてチェックという3つのチェックを実施します。
今回の実装にあたり、参考にさせていただいたサイトは最後にご紹介しております。

TaskCatとは?

TaskCatとはCloudFormationテンプレートのテストツールです。簡単なコマンドと設定ファイルで、複数のリージョンに対してデプロイのテストを実施可能です。また、テスト結果のレポートについてもHTML形式やテキストファイルにて生成してくれます。
調べてみたところ、有志の方が作成したのではなく、AWSのチーム(aws-ia)にて提供されているツールなんですね。。
TaskCatの公式サイトや開発チームのサイトは以下をご覧ください。

実装例

CodeCatalyst上の任意のスペースに、新規でプロジェクトを作成します。私は「TaskCat」というプロジェクトを作成しました。
本記事では以降の手順について解説します。

CloudFormationテンプレートファイルのソースコード管理

CodeCatalystのSource repositoriesを使用します。
今回はCodeCatalyst上で新規にリポジトリを作成します。
名前以外はデフォルトで設定しました。

CodeCatalystではGitHub・GitLab・Bitbucketのリポジトリを連携して使用することも可能です。
また、今回のファイル配置は以下のとおりです。
.
├ cfn_template
│ └ template.yaml.cfnlintrc.yaml
├ .gitignore
├ .taskcat.yml.yamllint.yaml
├ README.md
└ junit_xml.py

それぞれのファイルは下記のように作成しました。

template.yaml

CloudFormationでデプロイしたいテンプレートファイルです。
今回は一例として以下のようなテンプレートファイルを作成しました。

---
AWSTemplateFormatVersion: "2010-09-09"
Description: Sample CloudFormation Template

Parameters:
  vpcIpv4CicdBlock:
    Type: String
    Default: 10.0.0.0/16
  vpcNameTag:
    Type: String

Resources:
  myVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref vpcIpv4CicdBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Ref vpcNameTag

Outputs:
  myVpcId:
    Description: VPC ID
    Value: !Ref myVPC
    Export:
      Name: myVpcId

.cfnlintrc.yaml

CloudFormationテンプレートの構文チェックを実施する「cfn-lint」の設定ファイルです。

---
templates:
  - cfn_template/*.yaml

.taskcat.yml

テンプレートをデプロイしてチェックする「TaskCat」の設定ファイルです。
デプロイ先のリージョンの情報やパラメータの値を指定することができます。

---
project:
  name: sample-taskcat-project
  regions:
    - ap-northeast-1

tests:
  test-my-vpc:
    parameters: 
      vpcIpv4CicdBlock: 10.10.0.0/16
      vpcNameTag: test-vpc
    template: cfn_template/template.yaml

.yamllint.yaml

YAMLファイルの構文チェックを実施する「yamllint」の設定ファイルです。

---
yaml-files:
  - '*.yaml'
  - '*.yml'
  - '.yamllint'

rules:
  anchors: enable
  braces: enable
  brackets: enable
  colons: enable
  commas: enable
  comments:
    level: warning
  comments-indentation:
    level: warning
  document-end: disable
  document-start:
    level: warning
  empty-lines: enable
  empty-values: disable
  float-values: disable
  hyphens: enable
  indentation: enable
  key-duplicates: enable
  key-ordering: disable
  line-length: enable
  new-line-at-end-of-file: enable
  new-lines: enable
  octal-values: disable
  quoted-strings: disable
  trailing-spaces: enable
  truthy:
    level: warning

junit_xml.py

TaskCatのテスト結果(テキストファイル形式)をjunit形式に変換するためのPythonスクリプトです。
こちらは参考サイトよりそのまま使用させていただきました。
これを実施することにより、CodeCatalystのワークフロー上でテスト結果を簡単に確認することができます。

import sys
import os
import xml.etree.ElementTree as ET

def convert_to_junit_xml(log):
    # Create the root element
    testsuite = ET.Element("testsuite")

    # Create a testsuite element
    testsuite = ET.SubElement(testsuite, "testsuite", name="taskcat-cnf-development")

    # Parse the log and create testcases
    lines = log.split("\n")
    success_count = 0
    failure_count = 0
    for line in lines:
        if "CREATE_COMPLETE" in line or "CREATE_FAILED" in line:
            date, time, resource_status, resource_type, logical_resource_id, *rest = line.split()
            if len(rest) >= 6:
                resource_status_reason = ' '.join(rest)
            else:
                resource_status_reason = ""
            testcase = ET.SubElement(testsuite, "testcase", classname=resource_type, name=logical_resource_id)

            if resource_status == "CREATE_COMPLETE":
                ET.SubElement(testcase, "success")
                success_count += 1
            else:
                ET.SubElement(testcase, "failure", message=resource_status_reason)
                failure_count += 1

    # Add summary information
    testsuite.set("tests", str(success_count + failure_count))
    testsuite.set("failures", str(failure_count))

    # Return the string representation of the XML
    return ET.tostring(testsuite, encoding="unicode")

if __name__ == "__main__":
    # Check if the log file path is provided as a command line argument
    if len(sys.argv) < 2:
        print("Please provide the path to the log file as a command line argument.")
        sys.exit(1)

    # Read the log file
    log_file_path = sys.argv[1]
    with open(log_file_path, "r") as file:
        log = file.read()

    # Convert the log to JUnit XML format
    junit_xml = convert_to_junit_xml(log)
    print(junit_xml)

    # Save the XML as a file named "report.xml" in the same directory as the log file
    log_file_directory = os.path.dirname(log_file_path)
    report_file_path = os.path.join(log_file_directory, "report.xml")
    with open(report_file_path, "w") as file:
        file.write(junit_xml)

CloudFormationテンプレートのコーディング(実装)環境

CodeCatalystのDev Environmentsを使用します。
今回はVisual Studio Code上で操作可能な開発環境を設定します。
作成したリポジトリを取り込めるように設定します。

CloudFormationテンプレートのテンプレートチェック

CodeCatalystのWorkflowsを使用します。
ワークフローとしては非常にシンプルです。
mainブランチへのプルリクエストをトリガーにワークフローが動作するよう設定しました。

また、実際のワークフローのコード(YAMLファイル)を以下に示します。
今回は実行環境をLambdaではなくEC2で実行するようにします。

Name: CheckTemplate
SchemaVersion: "1.0"

# Optional - Set automatic triggers.
Triggers:
  - Type: PULLREQUEST
    Branches:
      - main
    Events:
      - OPEN

# Required - Define action configurations.
Actions:
  TestTemplateTask:
    # Identifies the action. Do not modify this value.
    Identifier: aws/build@v1.0.0
    # Specifies the source and/or artifacts to pass to the action as input.
    Inputs:
      # Optional
      Sources:
        - WorkflowSource # This specifies that the action requires this Workflow as a source
    Outputs:
      # Optional; Automatically discover reports for popular test frameworks
      AutoDiscoverReports:
        Enabled: true
        # Use as prefix for the report files
        ReportNamePrefix: rpt
    # Defines the action's properties.
    Configuration:
      # Required - Steps are sequential instructions that run shell commands
      Steps:
        - Run: pip install yamllint cfn-lint taskcat
        - Run: yamllint ./cfn_template
        - Run: cfn-lint
        - Run: taskcat test run
        - Run: report=`ls -rt taskcat_outputs/*.txt | tail -n 1`
        - Run: python3 junit_xml.py $report
      Container:
        Registry: CODECATALYST
        Image: CodeCatalystLinux_x86_64:2024_03
    Compute:
      Type: EC2
    Environment:
      Name: dev

実際に動かしてみた

では実際に開発→チェックまでの一連の流れを見てみましょう。

まずは、Dev EnvironmentsよりVisual Studio Codeの開発環境を起動します。
mainブランチより「dev」ブランチを作成し、その中で変更を実装していきます。
今回はAmazon Q Developerも活用してみます。
プロンプト入力画面を起動し、パブリックサブネットを追加してもらいましょう。

するとAmazon Qによりコードが生成されます。
パット見できていそうということで、変更を受け入れます。

Amazon Q Developerをはじめとした生成AIを用いた生成結果には誤りが含まれることがあります。
今回は一例を提示するのみでしたのでしっかりとは確認しませんでしたが、業務などで活用する際は生成結果をレビューするプロセスを挟むことを推奨します。

devブランチに変更をプッシュします。
これにより、devブランチが作成され、その中にパブリックサブネットを追加したテンプレートファイルが配置されています。

次に、devブランチからmainブランチにプルリクエストを作成します。
CodeCatalystのPull requestsより新規で作成します。
プルリクエストのタイトルは自分で入力し、説明文はAmazonQに記載してもらいます。
「Write description for me」のボタンを押下します。すると、変更文が生成されます。

生成された説明文は以下のとおりです。

The code change defines a public subnet, internet gateway, route table, and associated resources in an AWS CloudFormation template. The public subnet allows internet access for resources deployed within it. This configuration enables internet connectivity for resources deployed in the public subnet.

パブリックサブネットが生成された旨がしっかりと表示されていますね。このまま「Accept and add to description」を押下します。
最終的に下の「Create」ボタンを押下します。これによりプルリクエストが作成されました。

プルリクエストが作成されると、ワークフローの動作が開始します。完了するまで気長に待ちましょう。

今回はyamllintのプロセスでエラーが発生しました。インデントがズレているというエラーですね。
コードを確認してみると、確かにズレていたため、修正しもう一度実行してみます。

今回はしっかりと実行できました。

また、「Reports」タブを押下することで、変換されたテストの結果を確認することができます。

今回の構築例ですと、TaskCatで構築に失敗した場合に後続の変換処理(junit_xml.pyの実行)が行われずスキップされてしまい、NG項目について確認することができませんでした。失敗時にも後続処理を実行する方法が執筆時点でも見つけることができませんでした。これは今後の課題です。
最終的にdevブランチで作成されたテンプレートファイルに問題がないことを確認できたので、プルリクエストを受け入れます。
追加の設定は行わず、「Merge」を押下します。
これにより、mainブランチにdevブランチの変更が反映され、devブランチがクローズされました。

おわりに

今回はCodeCatalystを用いたCloudFormationテンプレートの開発環境をご紹介しました。
TaskCatのレポート機能などに課題は残っておりますが、個人的には割と使いやすいな…と感じた次第です。
TaskCat以外の部分の開発環境につきましては、CloudFormationテンプレートの開発以外にも活用できるので、改めてCodeCatalystの有用性を知ることができました。

Amazon Q Developerと一緒に開発できれば、一人の開発も怖くなさそうですね。。
もっと使いこなせるようになったらAmazon Q Developer Proも検討しようと思います。

参考サイト

今回は以下のサイトを参考にCodeCatalyst版のフローを作成しました。

タイトルとURLをコピーしました