AWS Transform Custom × CodeBuildでPythonランタイム更新を自動化してみた

記事タイトルとURLをコピーする

概要

この記事では 「AWS Transform CLIをCodeBuildで実行し、Lambda関数などのランタイム更新を自動化する方法」について紹介します。

【注意!】

本記事は2025年12月時点の情報をもとに作成しています。今後CLIのインストール方法や実行方法が変更される可能性があります。最新の情報は公式ドキュメントを参照してください。

【本記事でわかること】

  • AWS Transform CLIをカスタムDockerイメージにインストールする方法
  • ECRにカスタムイメージをプッシュする方法
  • CodeBuildでカスタムイメージを使用してビルドを実行する方法

はじめに

こんにちは!カスタマーサクセス部 CS1課の圡井です!

AWSの様々なサービスでは、セキュリティアップデートや新機能の追加に伴い、定期的にランタイムのバージョン更新が行われます。特にLambda関数では、古いランタイムがEOL(End of Life)を迎えると、アップデートが必須となります。

手動でのランタイム更新は、対象が少数の場合は問題ありませんが、多数のリソースを管理している場合、非常に時間がかかります。そこで、AWS Transformを使用してランタイム更新を自動化する方法を検討しました。

しかし、デフォルトのCodeBuildではAWS Transform CLIが含まれていなかったため、カスタムDockerイメージの作成から実施し、CodeBuildで使用する方法を紹介します。

AWS Transform Customの詳細、およびローカル環境での実施方法は、こちらの記事をご参照ください。

blog.serverworks.co.jp

前提条件

  • AWSアカウントとCLIの設定が完了していること
  • Dockerがローカル環境にインストールされていること
  • ECRリポジトリにアクセスする権限があること
  • CodeBuildプロジェクトを作成する権限があること

構成図

本記事では以下のような構成を構築します。

構成図

AWS Transform CLIとは

AWS Transformは、AIエージェントを活用して、Windows、メインフレーム、VMwareなど複雑な環境の評価、分析、リファクタリングを自動化し、効率的なモダナイズを支援するサービスです。

その中でも、AWS Transform Customはアプリケーションのモダナイズを加速するサービスです。

AWS Transform CLIを利用することでコマンドラインから操作することも可能です。

AWS Transform CLIをインストールしたDockerイメージの作成

Dockerfileの作成

まず、AWS Transform CLIをインストールしたDockerイメージを作成します。

# Python 3.13 公式イメージをベースに使用
FROM python:3.13-slim

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
    curl \
    git \
    unzip \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# AWS CLIのインストール
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
    && unzip awscliv2.zip \
    && ./aws/install \
    && rm -rf aws awscliv2.zip

# Node.jsのインストール
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
    && apt-get install -y nodejs \
    && rm -rf /var/lib/apt/lists/*

# AWS Transform CLIのインストール
RUN curl -fsSL https://desktop-release.transform.us-east-1.api.aws/install.sh -o install.sh \
    && chmod +x install.sh \
    && ./install.sh \
    && rm install.sh

# 非rootユーザーを作成
RUN useradd -m -s /bin/bash builduser

# インストールされたCLIを非rootユーザーがアクセスできる場所にコピー
RUN cp /root/.local/bin/atx /usr/local/bin/atx 2>/dev/null || \
    find / -name atx -type f -executable 2>/dev/null | head -1 | xargs -I {} cp {} /usr/local/bin/atx

# 非rootユーザーに切り替え
USER builduser

# 作業ディレクトリ
WORKDIR /home/builduser

【ポイント!】

  • Python 3.13をベースイメージとして使用していますが、プロジェクトの要件に応じて変更可能です。
  • AWS Transform CLIのインストールスクリプトは、/root/.local/binにCLIをインストールするため、非rootユーザーがアクセスできるように/usr/local/binにコピーしています。
  • セキュリティのベストプラクティスとして、非rootユーザーでコンテナを実行しています。

ローカルでの動作確認

イメージをビルドして、ローカルで動作確認を行います。

# Dockerイメージのビルド
$ docker build -t transform-cli-image .

# バージョン確認(認証情報を一時的にマウント)
$ docker run --rm \
  -v ~/.aws:/home/builduser/.aws \
  transform-cli-image atx --version
> Version: 1.0.1

正常にインストールされていれば、atx --versionコマンドでバージョン情報が表示されます。

【ポイント!】

  • ローカルで認証情報を使用するために、~/.awsディレクトリをコンテナ内の/home/builduser/.awsにマウントしています。
  • atxコマンドはログファイルを~/.aws/atx/logsに書き込むため、書き込み権限が必要です。
  • セキュリティを重視する場合は、認証情報ファイルのみをread-onlyでマウントし、ログディレクトリは別途マウントしてください。
  • 本番環境(CodeBuild)では、IAMロールを使用して認証するため、この設定は不要です。

ECRリポジトリの作成とイメージのプッシュ

ECRリポジトリの作成

AWS CLIを使用してECRリポジトリを作成します。

# ECRリポジトリの作成
$ aws ecr create-repository \
    --repository-name transform-cli-image \
    --region ap-northeast-1

ECRへのログインとイメージのプッシュ

作成したDockerイメージをECRにプッシュします。

# ECRにログイン
$ aws ecr get-login-password --region ap-northeast-1 | \
  docker login --username AWS --password-stdin <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com

# タグ付け
$ docker tag transform-cli-image:latest \
  <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/transform-cli-image:latest

# ECRにプッシュ
$ docker push <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/transform-cli-image:latest

【ポイント!】

  • <AWSアカウントID>は自分のAWSアカウントIDに置き換えてください。
  • リージョンは適宜変更してください。今回は東京リージョン(ap-northeast-1)を使用しています。

CodeBuildプロジェクトの作成

buildspec.ymlの作成

CodeBuildで実行するビルド定義ファイルを作成します。

version: 0.2

phases:
  pre_build:
    commands:
      - echo "Transform CLI version check"
      - atx --version

  build:
    commands:
      - echo "Build started on `date`"
      # ここにTransform CLIのコマンドを実行

  post_build:
    commands:
      - echo "Build completed on `date`"

artifacts:
  files:
    - '**/*'

このbuildspec.ymlは後ほど貼り付けるのでコピーしておきます。

CodeBuildプロジェクトの設定

AWSマネジメントコンソールまたはAWS CLIを使用してCodeBuildプロジェクトを作成します。

マネジメントコンソールでの作成手順:

項目 設定値
プロジェクト名 TransformCLI-Build
ソースプロバイダ ソースがありません
環境イメージ カスタムイメージ
コンピューティング EC2
環境タイプ Linuxコンテナ
イメージレジストリ Amazon ECR
ECRイメージ <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/transform-cli-image:latest
認証情報プルイメージ プロジェクトのサービスロール
Buildspec ビルドコマンドの挿入
先ほど保存したbuildspec.ymlを貼り付け
サービスロール 新しいサービスロール

【ポイント!】

  • カスタムイメージを使用する場合、「環境イメージ」で「カスタムイメージ」を選択します。
  • ECRからイメージをプルするため、サービスロールには自動的にECRへのアクセス権限が付与されます。

Transform対象のCodeCommitリポジトリを作成する

AWS Transformで変換するソースコードを格納するCodeCommitリポジトリを作成します。

CodeCommitリポジトリの作成

# CodeCommitリポジトリの作成
$ aws codecommit create-repository \
    --repository-name transform-scripts \
    --repository-description "AWS Transform変換スクリプト格納用" \
    --region ap-northeast-1

# リポジトリのクローン
$ git clone codecommit::ap-northeast-1://transform-scripts
$ cd transform-scripts

サンプルコードの作成

Python 3.9で動作するが、Python 3.13では非推奨となる機能を使用したサンプルコードを作成します。

ディレクトリ構造:

transform-scripts/
├── app.py
└── requirements.txt

app.py:

import datetime
from collections import Iterable, Mapping, OrderedDict
from typing import List, Dict, Optional, Tuple

class DataProcessor:

    def __init__(self, items: List[str], config: Dict[str, any]):
        self.items = items
        self.config = config
        self.data: OrderedDict = OrderedDict()

    def check_types(self, obj) -> Dict[str, bool]:
        return {
            "is_iterable": isinstance(obj, Iterable),
            "is_mapping": isinstance(obj, Mapping)
        }

    def get_datetime_info(self) -> Dict[str, datetime.datetime]:
        return {
            "current": datetime.datetime.utcnow(),
            "epoch": datetime.datetime.utcfromtimestamp(0)
        }

    def merge_dicts(self, dict1: Dict, dict2: Dict) -> Dict:
        result = dict1.copy()
        result.update(dict2)
        return result

def process_numbers(values: List[int]) -> Optional[Dict[str, float]]:
    if not values:
        return None
    return {
        "sum": sum(values),
        "avg": sum(values) / len(values),
        "max": max(values)
    }

if __name__ == "__main__":
    processor = DataProcessor(
        items=["item1", "item2"],
        config={"debug": True}
    )

    print(f"型チェック: {processor.check_types([1, 2, 3])}")
    print(f"日時情報: {processor.get_datetime_info()}")
    print(f"統計: {process_numbers([1, 2, 3, 4, 5])}")

requirements.txt:

boto3>=1.26.0,<2.0.0

使用している非推奨機能:

  • collections.Iterable/Mappingcollections.abc.Iterable/Mapping (Python3.10+)
  • datetime.datetime.utcnow()datetime.datetime.now(datetime.UTC) (Python3.12+)
  • datetime.datetime.utcfromtimestamp()datetime.datetime.fromtimestamp(, tz=datetime.UTC) (Python3.12+)
  • typing.List/Dict/Tuplelist/dict/tuple (Python3.9+)

リポジトリにプッシュ

$ git add .
$ git commit -m "Add sample Python 3.9 code with deprecated features"
$ git push origin main

CodeBuildサービスロールに権限を追加

CodeBuildがCodeCommitリポジトリにアクセスし、AWS Transformを実行できるように権限を追加します。

CodeCommitアクセス権限

CodeBuildのサービスロールにインラインポリシーを追加します。

ポリシー名: CodeCommitAccessPolicy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "codecommit:GitPull",
        "codecommit:GitPush",
        "codecommit:GetBranch",
        "codecommit:GetCommit",
        "codecommit:GetRepository",
        "codecommit:ListBranches",
        "codecommit:CreateBranch",
        "codecommit:PutFile"
      ],
      "Resource": "arn:aws:codecommit:ap-northeast-1:*:transform-scripts"
    }
  ]
}

AWS Transform Custom権限

AWS Transform Customを実行するための権限を追加します。

ポリシー名: TransformCustomAccessPolicy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "transform-custom:ConverseStream",
        "transform-custom:GetTransformation",
        "transform-custom:StartTransformation"
      ],
      "Resource": "*"
    }
  ]
}

【ポイント!】

  • GitPush権限は、変換後のコードを新しいブランチにプッシュするために必要です。
  • Transform Custom権限は、AWS Transformのカスタム変換を実行するために必要です。

最終的なbuildspec.yml

CodeBuildで実行する完全なbuildspec.ymlです。このファイルは、リポジトリのクローン、Transform実行、変更のプッシュまでを自動化します。

version: 0.2

phases:
  pre_build:
    commands:
      - echo "Transform CLI version check"
      - atx --version

      # Git認証情報設定
      - git config --global credential.helper '!aws codecommit credential-helper $@'
      - git config --global credential.UseHttpPath true
      - git config --global user.name "AWS CodeBuild"
      - git config --global user.email "codebuild@example.com"

      # CodeCommitリポジトリをクローン
      - echo "Cloning transform-scripts repository..."
      - git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/transform-scripts /transform-scripts
      - cd /transform-scripts
      - git branch -a
      - CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
      - echo "Current branch is $CURRENT_BRANCH"

  build:
    commands:
      - echo "Build started on `date`"
      - cd /transform-scripts

      # アップデート対象の設定ファイルを作成
      - |
        cat > atx-config.yaml << 'EOF'
        codeRepositoryPath: .
        transformationName: AWS/python-version-upgrade
        additionalPlanContext: |
          The target Python version to upgrade to is Python 3.13.
        EOF

      # AWS Transform実行
      - echo "Running AWS Transform..."
      - atx custom def exec --configuration file://atx-config.yaml --non-interactive --trust-all-tools

      # Transformの実行結果を確認
      - |
        echo "Checking git status after Transform..."
        git status
        git log --oneline -3

      # Transformログファイルをコピー
      - |
        echo "Copying Transform logs..."
        mkdir -p .transform-logs
        if [ -d /root/.aws/atx/custom ]; then
          LATEST_LOG_DIR=$(ls -td /root/.aws/atx/custom/*/ 2>/dev/null | head -1)
          if [ -n "$LATEST_LOG_DIR" ]; then
            echo "Found logs in: $LATEST_LOG_DIR"
            cp -r "$LATEST_LOG_DIR"* .transform-logs/ 2>/dev/null || echo "No log files to copy"
          fi
        fi

      # 新しいブランチを作成してプッシュ
      - |
        # 新しいブランチ名を生成
        TRANSFORM_BRANCH="transform/python-3.13-$(date +%Y%m%d-%H%M%S)"
        echo "Creating new branch: $TRANSFORM_BRANCH"
        git checkout -b $TRANSFORM_BRANCH

        # ログファイルを追加してコミット
        git add .transform-logs
        if ! git diff --cached --quiet; then
          git commit -m "Add Transform execution logs"
          echo "Added Transform logs."
        fi

        # ブランチをプッシュ
        echo "Pushing branch to remote..."
        git push origin $TRANSFORM_BRANCH
        echo "Successfully pushed to branch: $TRANSFORM_BRANCH"

artifacts:
  files:
    - '**/*'
  base-directory: /transform-scripts

【ポイント!】

  • atx-config.yamlファイルで、変換の設定を指定しています。
    • codeRepositoryPathは変換対象のディレクトリを指定します。
    • transformationNameは使用する変換テンプレートを指定します。
    • additionalPlanContextで、Python 3.13へのアップグレードを指示しています。
      この時、変換以外の指示を含めないよう注意してください。
  • atx custom def execコマンドで、Python 3.13へのアップグレード変換を実行します。
    • --configurationで、変換の指示を記載したコンフィグファイルを指定します。
    • --non-interactiveは、対話的なプロンプトなしで実行するオプションです。
    • --trust-all-toolsは、すべてのツールを信頼して実行するオプションです。
  • Transformログは/root/.aws/atx/customに保存されるため、リポジトリにコピーして記録します。
  • Pythonキャッシュファイル(__pycache__, *.pyc)は不要なので削除します。

動作確認

CodeBuildの実行

AWSマネジメントコンソールからCodeBuildプロジェクトを実行します。

  1. CodeBuildコンソールを開く
  2. 作成したプロジェクト(TransformCLI-Build)を選択
  3. 「ビルドを開始」をクリック

実行ログの確認

ビルドログで以下の内容を確認します。 今回のコード量では20分程度で完了しました。 CPU使用率は平均20%程度、メモリ使用率も152MB程度で推移しており、最小スペックでも処理をすることができました。

AWS Transform CLIコマンドの実行後は、Using tool: ...のログが表示され、ファイルリードや変換、コミットなど様々な処理が実行されていることがわかります。

[Container] Running command atx --version
Version: 1.0.1

~略~

[Container] Running command git clone https://git-codecommit...
Cloning transform-scripts repository...

~略~

[Container] Running command atx custom def exec...
┌──────────────────────────────────────────────────────────────────────────────┐
│   🟡 All tools are now trusted. ATX will execute tools without asking for    │
│                                confirmation.                                 │
└──────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│               🏢 You are using an AWS Managed transformation.                │
└──────────────────────────────────────────────────────────────────────────────┘

🔧  Using tool: file_read (trusted)
 ⋮
 ● Mode: "read"
 ● Path: "app.py"
 ⋮
 ● Completed in 0ms

~略~

[Container] Running command git push origin transform/python-3.13-20251225-100224
To https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/transform-scripts
 * [new branch]      transform/python-3.13-20251225-100224 -> transform/python-3.13-20251225-100224

変換結果の確認

CodeCommitコンソールで、新しく作成されたブランチを確認します。

  1. CodeCommitコンソールを開く
  2. transform-scriptsリポジトリを選択
  3. ブランチ一覧からtransform/python-3.13-YYYYMMDD-HHMMSSブランチを確認
  4. 変更内容を確認:
    • app.pyが更新されている
    • requirements.txtが更新されている
    • .transform-logs/ディレクトリに実行ログが含まれている

変更例:

# 変更前
from collections import Iterable, Mapping
from typing import List, Dict

# 変更後
from collections import OrderedDict
from collections.abc import Iterable, Mapping
from typing import Optional, Any

# 変更前
def __init__(self, items: List[str], config: Dict[str, any]):

# 変更後
def __init__(self, items: list[str], config: dict[str, Any]):

# 変更前
"current": datetime.datetime.utcnow(),
"epoch": datetime.datetime.utcfromtimestamp(0)

# 変更後
"current": datetime.datetime.now(datetime.timezone.utc),
"epoch": datetime.datetime.fromtimestamp(0, tz=datetime.timezone.utc)

実際のブランチには以下のようなファイルが作成されます。

作成されるファイル群

プルリクエストの作成

変換が正常に完了し、更新に納得することができれば、CodeCommitでプルリクエストを作成してレビューします。

ランタイム更新は機械的な置換に見えても、依存ライブラリや実行環境差分の影響でトラブルが起きやすい領域です。 そのため、いきなりmainブランチへ直接プッシュするのではなく、まず変換結果を新しいブランチにプッシュすることで、プルリクエスト経由で差分レビューとリグレッションテスト(自動/手動)を通してからマージできるようにしています。

  1. CodeCommitコンソールで「プルリクエスト」を選択
  2. 「プルリクエストの作成」をクリック
  3. ソースブランチ:transform/python-3.13-yyyymmdd-HHMMSS
  4. ターゲットブランチ:main
  5. タイトル:「Python 3.13へのアップグレード」
  6. 変更内容をレビューしてマージ

【ポイント!】

  • Transform実行ログを確認することで、どのような変更が行われたかを把握できます。
  • プルリクエストでレビューすることで、意図しない変更がないか確認できます。
  • 新規ブランチ→プルリクエスト経由にすることで、リグレッションテストの実施や承認プロセスを挟んだうえで安全に取り込めます。
  • 本番環境に適用する前に、テスト環境で動作確認することを推奨します。

検証時に発生したトラブルシューティング

実行時間が長い

問題:

CodeBuildの実行に予想以上の時間がかかる。

原因:

Dockerイメージ内にPython実行環境がインストールされていない場合、Transform実行時にPythonのインストールが行われるため、時間がかかります。

解決方法:

本記事で紹介したDockerfileでは、Python 3.13がプリインストールされているため、この問題は発生しません。

Transform実行エラー

問題:

atx custom def execコマンドが失敗する。

原因:

  • AWS Transform Custom権限が不足している
  • additionalPlanContextに変換以外の内容が含まれている

解決方法:

  • サービスロールにTransform Custom権限が付与されているか確認
  • additionalPlanContextには、変換の指示のみを記載する
# ❌ NGな例
additionalPlanContext: |
  The target Python version to upgrade to is Python 3.13.
  Also update the documentation.
  Fix all bugs.

# ✅ OKな例
additionalPlanContext: |
  The target Python version to upgrade to is Python 3.13.

Git Push権限エラー

問題:

git push時に権限エラーが発生する。

原因:

CodeBuildのサービスロールにCodeCommitへのPush権限がない。

解決方法:

サービスロールにcodecommit:GitPush権限を追加します(本記事のIAMポリシーを参照)。

まとめ

本記事では、AWS Transform CLIをCodeBuildで使用して、Lambda関数などのランタイム更新を自動化する方法を紹介しました。

ポイントのまとめ:

  1. カスタムDockerイメージの作成

    • AWS Transform CLIは標準のCodeBuildイメージには含まれていない
    • Python環境を含むカスタムイメージを作成してECRにプッシュすることで解決
    • 非rootユーザーで実行する場合はPATHの設定に注意
  2. CodeCommitとの連携

    • Transform対象のコードをCodeCommitで管理
    • CodeBuildから自動的にクローン、変換、プッシュを実行
    • 新しいブランチに変更をプッシュし、プルリクエスト経由でレビュー・リグレッションテスト後にmainへ取り込む
  3. 権限管理

    • CodeCommitへの読み書き権限
    • AWS Transform Customの実行権限
    • ECRへのアクセス権限
  4. 自動化のメリット

    • 手動でのランタイム更新作業が不要になる
    • 複数のLambda関数を一括で更新可能
    • 変更履歴がGitで管理される
    • プルリクエストでレビューフローを組み込める

この方法を活用することで、Lambda関数のランタイム更新などを効率的に自動化できるようになります。

今回やってみた感想としては、テストフェーズまでも自動化してくれる点がとても有用だと感じました! 出力されたファイル群を見て、十分なテストが行われていそうであれば、のちの工程に素早くうつることができそうです!

一方で、深堀できそうなポイントとしては、バージョン更新以外の指示がうまくできなかった点です。 自動的なテストだけでなく、そのコードの役割を示したテストファイルを組み込むことができれば、より自動化として価値のあるものになると感じました!

定期的なランタイム更新は、セキュリティアップデートや新機能の活用に不可欠です。本記事の方法により、運用負荷を大幅に削減しつつ、確実にアップデートを実施できます。

最後までお読みいただき、ありがとうございました!

圡井一磨(執筆記事の一覧)

23年度新卒入社しました。最近は自炊にはまっています。アパートのキッチンが狭くて困ってます。