G-gen の杉村です。当記事では、AWS Lambda のサーバーレス関数から Google Cloud の Cloud Storage にファイルをアップロードする方法をご紹介します。認証・認可には Workload Identity を利用します。
- はじめに
- [A] IAM ロール作成・権限付与
- [G] Cloud Storage バケット作成
- [G] サービスアカウント作成
- [G] サービスアカウントへの権限付与
- [G] Workload Identity Pool 作成
- [G] Workload Identity Pool にサービスアカウントを接続
- [L] デプロイパッケージ作成
- [A] Lambda 関数デプロイ
- [A] S3 バケット作成・動作テスト

はじめに
検証の概要
当記事では、Amazon Web Services(AWS)の AWS Lambda 関数から、Google Cloud の Cloud Storage バケットにファイルをアップロードする方法をご紹介します。
AWS Lambda 関数はサーバーレスプラットフォームであり、Python や Node.js のような各種言語のプログラムを動作させることができます。プログラムに Google Cloud のクライアントライブラリ(Cloud SDK)を組み込めば、Google Cloud の API へリクエストを送信して、Cloud Storage 等にデータを投入することが可能です。
留意事項
当記事では簡易化のため、IAM 権限の割り当てに関する考慮を最小限にしています。実際には最小権限の原則に基づき、適切な権限設定をしてください。
またサンプルコードでは例外処理やロギングに関する考慮を省略しています。実際には適切な考慮が必要です。
構成
当記事では、以下のような構成で検証を行います。

- Amazon Web Services (AWS)
- Amazon S3 バケット
- AWS Lambda 関数(Python)
- IAM ロール
- Google Cloud
- Cloud Storage バケット
- Workload Identity Pool
- サービスアカウント
AWS から Google Cloud への認証
クロスクラウドのデータ移送では、認証と認可がポイントになります。
Google Cloud API へリクエストを行うには、Google アカウントまたはサービスアカウントの認証情報が必要です。まず思いつく方法は「Google Cloud でサービスアカウントを作成する。サービスアカウントキー(秘密鍵)を発行する。これを AWS Secrets Manager に保存する。キーを Lambda プログラムから参照して、Google Cloud への認証に用いる」といった方法でしょう。
- 参考 : Google での認証方法 - サービス アカウント
- 参考 : Authentication - google-api-core documentation - Service Accounts
しかしこれには、キーローテーションの手間や、キー漏洩の危険性がつきまといます。
当記事では Workload Identity を使うことで、サービスアカウントキーを発行することなく、AWS から Google Cloud へ認証する方法を検証します。
Workload Identity の概要
Workload Identity では信頼する対象の AWS アカウントと IAM ロール名等を指定することで、AWS の IAM プリンシパルが Google Cloud サービスアカウントの権限を借用することを許可します。
Google Cloud 側では、サービスアカウントに必要な IAM 権限を付与しておきます。AWS の IAM ロール等は、そのサービスアカウントから権限を借用して、Google Cloud API へリクエストを実行できます。

なお「権限の借用」とは、実際には OAuth 2.0 仕様に従ったトークン交換を指します。一時的なトークンが Google Cloud から AWS へ払い出されることで、これを用いて AWS 環境から Google Cloud API へのリクエストが可能になります。
構築の流れ
以下の流れで上記環境を構築します。
- [AWS] IAM ロールを作成・権限付与
- [Google Cloud] Cloud Storage バケットを作成
- [Google Cloud] サービスアカウントを作成・権限付与
- [Google Cloud] Workload Identity Pool を作成
- [Google Cloud] Workload Identity Pool にサービスアカウントを接続
- [ローカル] デプロイパッケージを作成
- [AWS] Lambda 関数をデプロイ
- [AWS] S3 バケット作成
- [AWS] 動作テスト
以降では、見出しの [G] は Google Cloud 側での作業を、[A] は AWS 側での作業を、[L] はローカル環境での作業を意味します。
[A] IAM ロール作成・権限付与
まずは、Lambda 関数にアタッチするための IAM ロールを作成します。Google Cloud 側の設定で、信頼する対象の IAM ロールとして名称が必要なため、先に作成します。
AWS マネジメントコンソールの IAM の画面に遷移し、左部メニューから ロール を選択します。
ボタン「ロールを作成」を押下します。
「信頼されたエンティティタイプ」として「AWS のサービス」を、ユースケースとして「Lambda」を選択し、ボタン「次へ」を押下します。

次のステップ 許可を追加 では、付与する IAM ポリシーとして以下を追加します。
AWSLambdaBasicExecutionRoleAmazonS3ReadOnlyAccess
後者の AmazonS3ReadOnlyAccess については、AWS アカウント内の全てのバケットの全オブジェクトに読取アクセスができてしまう強力な権限です。実際のプロジェクトでは、対象バケットを絞った独自の IAM ポリシーを作成することを推奨します。
次のステップではロール名を s3-to-gcs-role とし、最下部のボタン「ロールを作成」を押下します。
[G] Cloud Storage バケット作成
Google Cloud コンソールで Cloud Storage 画面へ遷移し、Cloud Storage バケットを作成します(Cloud Storage > バケット > ボタン「作成」)。
今回のバケット名は my-target-bucket-01 とします。

[G] サービスアカウント作成
次に IAM と管理 画面の左部メニューから サービス アカウント を選択して画面遷移し、サービスアカウントを作成します。(IAM と管理 > サービス アカウント > ボタン「サービス アカウントを作成」)
今回のサービスアカウント名は s3-to-gcs とします。割り当てられるメールアドレスは s3-to-gcs@(your-pj-name).iam.gserviceaccount.com となりました。
なおウィザードの中で以下のように「このサービス アカウントにプロジェクトへのアクセスを許可する」というプロセスがありますが、ここでは何の権限もつけなくても構いません。次のステップで、権限を付与します。

[G] サービスアカウントへの権限付与
再び Cloud Storage 画面へ遷移し、先程作成したサービスアカウントに、対象の Cloud Storage バケットへのオブジェクト管理権限を付与します。
バケット一覧画面から先程のバケット名を選択したあと、権限 タブへ移動します。画面中程にあるボタン「アクセス権を付与」をクリックします。

新しいプリンシパル に先ほど作成したサービスアカウントのメールアドレスを、ロールとして Storage オブジェクト管理者 を割り当てます。

なお必要な処理がアップロードだけであれば Storage オブジェクト作成者 でも構いません。この権限の場合、オブジェクトの読み取り(ダウンロード)や削除、上書きはできません。なお、Cloud Storage のおいては、上書き処理は「一度削除してから再度書き込み」という処理と同等です。
[G] Workload Identity Pool 作成
IAM と管理 画面の左部メニューから Workload Identity 連携 を選択します。
ボタン「プールを作成」を押下し、新しい Workload Identity プールを作成します。Workload Identity プールとは、Workload Identity 設定の管理単位です。
作成画面を遷移すると以下の順で入力することになります。今回は以下のような値を入力しました。各名称は任意ですので、実際の環境では適切な名称を付けてください。
| 設定名 | 値 |
|---|---|
| 名前 | my-aws-environment |
| プール ID | my-aws-environment |
| プロバイダの選択 | AWS |
| プロバイダ名 | my-aws-account |
| プロバイダ ID | my-aws-account |
| AWS アカウント ID | (自分の AWS アカウント ID を入力) |
なお最後に「プロバイダ属性を構成する」がありますが、ここは何も手を加えずにボタン「保存」を押下します。

ここで詳細な属性マッピングを構成することもできますが、デフォルトのままで通常の用途には足ります。詳細は以下のドキュメントをご参照ください。
[G] Workload Identity Pool にサービスアカウントを接続
Workload Identity プール作成が完了すると、プールの詳細画面に遷移します。
上部のボタン「アクセスを許可」を押下し、このプールとリンクさせるサービスアカウントを選択します。
先程作成したサービスアカウント s3-to-gcs を選択します。
プリンシパル(サービス アカウントにアクセスできる ID)の選択 では、デフォルトでは プール内のすべての ID が選択されています。この状態だと、先程指定した AWS アカウント ID 内の全てのプリンシパル(IAM ユーザーや IAM ロール等)がサービスアカウント権限を借用できるようになります。
フィルタに一致する ID のみ を選択し、属性名として aws_role を選択、属性値に arn:aws:sts::(AWS Account ID):assumed-role/(IAM ロール名) を入力することで、特定の IAM ロールだけを許可することもできます。

ボタン「保存」を押下すると、構成ファイルのダウンロードができます。このファイルは単なる設定ファイルであり、機密データは含まれていません。このファイルをデプロイパッケージに入れることになります。プロバイダとして先程の my-aws-account を選択して、ボタン「構成をダウンロード」を押下し、ローカル環境にダウンロードしてください。
[L] デプロイパッケージ作成
サンプルコード
Lambda 関数のソースコードは以下を使います。繰り返しになりますが、以下はあくまでサンプルコードであり、実際には例外処理やロギング、セキュアなコーディングに関する考慮をしたうえでご利用ください。
import boto3 from google.cloud import storage def get_from_s3(s3_bucket_name, s3_object_name): # クライアント生成等 s3 = boto3.client('s3') s3_object_path=f"my_s3_path/{s3_object_name}" tmp_file_path=f"/tmp/{s3_object_name}" # ファイルを Lambda の一時領域にダウンロード s3.download_file(s3_bucket_name, s3_object_path, tmp_file_path) print(f"{s3_bucket_name}/{s3_object_path} was downloaded to {tmp_file_path}.") return tmp_file_path def upload_to_gcs(tmp_file_path, gcs_bucket_name): # クライアント生成等 gcs = storage.Client() file_name=tmp_file_path.split('/')[-1] gcs_object_path=f"my_gcs_path/{file_name}" bucket = gcs.bucket(gcs_bucket_name) blob = bucket.blob(gcs_object_path) # オブジェクトを Cloud Storage バケットにアップロード blob.upload_from_filename(tmp_file_path) print(f"{tmp_file_path} was uploaded to {gcs_bucket_name}/{gcs_object_path}.") return None def lambda_handler(event, context): # event から各種情報を取得 s3_bucket_name=event['s3_bucket_name'] s3_object_name=event['s3_object_name'] gcs_bucket_name=event['gcs_bucket_name'] # オブジェクトを S3 バケットから取得 tmp_file_path=get_from_s3( s3_bucket_name=s3_bucket_name, s3_object_name=s3_object_name ) # オブジェクトを Cloud Storage バケットにアップロード upload_to_gcs( tmp_file_path=tmp_file_path, gcs_bucket_name=gcs_bucket_name ) return {'statusCode': 200}
この Lambda 関数のソースコードは、以下のような処理を行います。
- event として
s3_bucket_names3_object_pathgcs_bucket_nameを受け取るs3_bucket_name: ファイル取得元の S3 バケットs3_object_name: 取得対象の S3 オブジェクト名gcs_bucket_name: アップロード先の Cloud Storage バケット名
- Amazon S3 バケットの
my_s3_path/パスからオブジェクトを Lambda 内の /tmp にダウンロード - /tmp にダウンロードしたオブジェクトを Cloud Storage の
my_gcs_path/パスにアップロード
クライアントライブラリの詳細な使用方法については、以下の公式ドキュメントを参照してください。
パッケージング
ローカル環境で、新規ディレクトリを作成してそのディレクトリに移動したあと、上記ソースコードを lambda_function.py として配置します。
また先程ダウンロードした Workload Identity Pool の構成ファイルも同じディレクトリに配置します。
Lambda では、Google Cloud クライアントライブラリのような外部ライブラリはデプロイパッケージに含ませる必要があるため、以下のコマンドでディレクトリ内に展開します。
pip install google-cloud-storage -t .
pip コマンドの -t オプションは、指定ディレクトリに Python パッケージをダウンロードできるオプションです。 -t . によりカレントディレクトリに、依存関係を含む各種モジュールが展開されます。
次に以下のコマンドで1段階上位のディレクトリに zip ファイルを作成します。
zip -r ../function.zip .
[A] Lambda 関数デプロイ
以下のコマンドで作成した zip パッケージを Lambda 関数としてデプロイします。aws lambda create-function コマンドを用います。
Lambda 関数には環境変数を持たせることができます。Workload Identity 構成ファイル名と、Google Cloud プロジェクト ID を環境変数として持たせます。以下コマンドの最初の3行はご自身の環境の値に置き換えてください。
AWS_ACCOUNT_ID="123456789012" CONFIG_FILE_NAME="clientLibraryConfig-my-aws-account.json" GOOGLE_CLOUD_PROJECT_ID="my-project-id" aws lambda create-function \ --function-name s3-to-gcs \ --zip-file fileb://../function.zip \ --handler lambda_function.lambda_handler \ --role arn:aws:iam::${AWS_ACCOUNT_ID}:role/s3-to-gcs-role \ --runtime python3.10 \ --region ap-northeast-1 \ --environment "Variables={GOOGLE_APPLICATION_CREDENTIALS=${CONFIG_FILE_NAME},GOOGLE_CLOUD_PROJECT=${GOOGLE_CLOUD_PROJECT_ID}}" \ --timeout 120
一度デプロイしたあとの関数は、以下のコマンドで修正できます。もちろん、コンソール画面から修正することもできます。
[A] S3 バケット作成・動作テスト
Lambda の Web コンソール画面でデプロイした関数を選択し、テスト タブから今回の関数の動作確認を行うことができます。
事前に適当な S3 バケットを作成し、my_s3_path フォルダ配下に test.txt を配置します。
その後、以下の JSON を イベント JSON に入力し、右上のボタン「テスト」を押下して動作テストを実行してください。JSON の値は自環境のものに置き換えます。
{
"s3_bucket_name": "my-test-bucket",
"s3_object_name": "test.txt",
"gcs_bucket_name": "my-target-bucket-01"
}

画面上に標準出力や標準エラー出力が表示されます。実際に Cloud Storage バケットの中身も確認してください。
杉村 勇馬 (記事一覧)
執行役員 CTO
元警察官という経歴を持つ IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 認定資格および Google Cloud 認定資格はすべて取得。X(旧 Twitter)では Google Cloud や Google Workspace のアップデート情報をつぶやいています。
Follow @y_sugi_it