GitHub ActionsでオープンデータをS3にアップロードしてLambdaで処理する
先日、GitHubで公開しているオープンデータを AWS内で処理する設定を行いました。
その過程を若干一般化して投稿したいと思います。
AWS側の処理はオープンデータをS3にアップロードして、Lambdaで実行する流れになります。
S3へのアップロードをトリガーにLambdaを動かすこともできますが、既にファイルパスを渡すとS3のファイルを取得して処理するものを作成していますので、S3へのアップロード後にGitHub側からLambdaを呼び出すことにしました。
概要
GitHub Actions で AWSのリソースを使えるようにするには、AWS側とGitHub側双方に設定が必要です。
具体的には以下の設定が必要になります。
-
Amazon Web Service 設定
1-1. IDプロバイダ追加
1-2. IAMロール作成
1-3. IAMポリシー作成 -
GitHub 設定
2-1. シークレット変数作成
2-2. ワークフローファイル作成
2-3. 実行テスト
それぞれはそんなに難しいものではないので、順に説明していきます。
1. Amazon Web Service設定
AWS側の設定は、ここではWebコンソール上で行っていきます。
1-1. IDプロバイダ追加
IAMのページから以下を開きます。
アクセス管理 > IDプロバイダ
次に プロバイダの追加
ボタンをクリックします。
IDプロバイダの作成 ページで以下を入力します。
項目 | 値 |
---|---|
プロバイダのタイプ | OpenID Connect |
プロバイダの URL | https://token.actions.githubusercontent.com |
対象者 | sts.amazonaws.com |
次に、サムプリントを取得
ボタンをクリックして、サムプリントを取得します。
正しく取得されたら、プロバイダを追加
ボタンをクリックします。
これで IDプロバイダが作成されます。
1-2. IAMロール作成
次にIAMロールを作成します。
アクセス管理 > ロール
を開いて、ロールを作成
ボタンをクリックします。
ステップ 1 信頼されたエンティティ を選択のページでは以下を入力して、次へ
をクリックします。
項目 | 値 |
---|---|
信頼されたエンティティタイプ | ウェブアイデンティティ |
アイデンティティプロバイダー | token.actions.githubusercontent.com |
Audience | sts.amazonaws.com |
ステップ 2 許可を追加 では IAMポリシーを設定しますが、後で説明するのでここでは何も選択せず次へ
をクリックします。
ステップ 3 名前、確認、および作成 では ロールの名前を付けて、ロールを作成
ボタンをクリックします。
これでIAMロールは作成されますがこのままでは使えないようなので、もう一度、アクセス管理 > ロール
を開いて、作成したロールのページを開きます。
信頼関係
タブを開くと、以下のようなJSONが表示されると思います。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::[AWSアカウントID]:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
}
ここの Condition
以下の部分を書き換える必要があります。信頼ポリシーを編集
ボタンをクリックして以下のように書き換えます。
これで、特定のリポジトリの特定のブランチのみで、AWSリソースを使えるようにできます。
[GitHubアカウント名]
、[リポジトリ名]
、[対象とするブランチ名]
は使用したいアカウント、リポジトリ、ブランチの名前に置き換えてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::[AWSアカウントID]:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:[GitHubアカウント名]/[リポジトリ名]:ref:refs/heads/[対象とするブランチ名]"
}
}
}
]
}
後述の設定ではmain
ブランチを使っていますが、それであれば以下のようになります。
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:[GitHubアカウント名]/[リポジトリ名]:ref:refs/heads/main"
}
}
また、StringEquals
を StringLike
に変更することでワイルドカード*
が使用でき、ref:refs/heads/main
を *
に置き換えれば、main
ブランチ以外でもAWSのリソースを使えるようにできます。
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:[GitHubアカウント名]/[リポジトリ名]:*"
}
}
このIAMロールのARNは後ほどGitHub側で使います。
1-3. IAMポリシー作成
最後にIAMポリシーを作成します。IAMポリシーは今回使用する S3へのアップロードs3:putObject
と Lambdaの呼び出し lambda:InvokeFunction
のみ許可するようにしました。
もし S3のputイベントをトリガーにLambdaを動かすのであれば、lambda:InvokeFunction
は必要ないです。
作成したIAMポリシーをIAMロールにアタッチしておきます。
これで IAMロールで設定した GitHubのリポジトリの指定したブランチから、許可したリソースを使えるようになるはずです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "arn:aws:s3:::[S3バケット名]/*"
},
{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": "[LambdaファンクションのARN]"
}
]
}
2. GitHub設定
ここからは GitHub側の設定です。
2-1. シークレット変数作成
GitHub リポジトリページ内にシークレット変数を作成します。
Settingsタブの
General > Security > Secret > Actions
を開きます。
New repository secret
ボタンを押すとシークレット変数を作成できます。
ここに設定した変数は、GitHub Actions のログには表示されません(マスクされる)。
これでパブリックリポジトリでもAWS側の設定が漏洩することはないはずです。
変数名 | 値 |
---|---|
AWS_ROLE_ARN | IAMロールのARN |
AWS_REGION | リージョン(例: ap-northeast-1 ) |
BUCKET_NAME | S3バケット名 |
FUNCTION_NAME | Lambdaファンクション名 |
2-2. ワークフローファイル作成
GitHub Actions のワークフローファイルをリポジトリに作成して、pushします。
name: CI
on:
push:
branches:
- main
jobs:
check-secret:
runs-on: ubuntu-latest
outputs:
my-key: ${{ steps.my-key.outputs.defined }}
steps:
- id: my-key
if: "${{ env.MY_KEY != '' }}"
run: echo "::set-output name=defined::true"
env:
MY_KEY: ${{ secrets.AWS_ROLE_ARN }}
deploy:
runs-on: ubuntu-latest
needs:
- check-secret
if: needs.check-secret.outputs.my-key == 'true'
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: tj-actions/changed-files@v18
id: changed-files
with:
files: |
**/*.ttl
- uses: aws-actions/configure-aws-credentials@v1
if: steps.changed-files.outputs.any_changed == 'true'
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Upload all changed files
if: steps.changed-files.outputs.any_changed == 'true'
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
echo "$file was changed"
aws s3 cp $file s3://${{ secrets.BUCKET_NAME }}/$file
echo '{"file": "/'$file'" }' > payload.json
aws lambda invoke --function-name ${{ secrets.FUNCTION_NAME }} --payload "$(cat payload.json | base64)" response.json
cat response.json
done
このワークフローファイルについては、以下のように記述しています。
main
ブランチに push したときにワークフローを実行します。
on:
push:
branches:
- main
シークレット変数 AWS_ROLE_ARN
が定義されているかどうかチェックします。
定義がなければ、deploy
の処理をスキップします。これで、誰かがこのリポジトリをフォークしても シークレット変数が定義されていないことで GitHub Actions がエラーになることが避けられると思います。
check-secret:
runs-on: ubuntu-latest
outputs:
my-key: ${{ steps.my-key.outputs.defined }}
steps:
- id: my-key
if: "${{ env.MY_KEY != '' }}"
run: echo "::set-output name=defined::true"
env:
MY_KEY: ${{ secrets.AWS_ROLE_ARN }}
deploy:
runs-on: ubuntu-latest
needs:
- check-secret
if: needs.check-secret.outputs.my-key == 'true'
追加・更新のあったファイルを検出します。
ここでは ttl
拡張子を持つファイルのみを抽出する設定にしています。
これにより、README.md
など対象でないファイルの処理を除外することができます。
- uses: tj-actions/changed-files@v18
id: changed-files
with:
files: |
**/*.ttl
AWSのリソースを使えるようにIAMロールを指定します。
if: steps.changed-files.outputs.any_changed == 'true'
で 更新ファイルにttl
ファイルがあるかどうかをチェックしています。
なければ、スキップします。
- uses: aws-actions/configure-aws-credentials@v1
if: steps.changed-files.outputs.any_changed == 'true'
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ secrets.AWS_REGION }}
更新のあった ttl
ファイルを S3バケットにアップロードして、Lambdaファンクションを呼び出します。
lambdaファンクションの payload の指定は Base64エンコードが必要みたいなので、--payload "$(cat payload.json | base64)"
としています。
- name: Upload all changed files
if: steps.changed-files.outputs.any_changed == 'true'
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
echo "$file was changed"
aws s3 cp $file s3://${{ secrets.BUCKET_NAME }}/$file
echo '{"path": "/'$file'" }' > payload.json
aws lambda invoke --function-name ${{ secrets.FUNCTION_NAME }} --payload "$(cat payload.json | base64)" response.json
cat response.json
done
2-3. 実行テスト
作成したワークフローファイルと、アップロードしたい ttl
ファイル、正しくアップロード除外されるかのテストのためttl
ファイル以外を push してみます。
例えば、以下の3つのファイルをpushします。
.github/workflows/ci.yml
go2museum/museum.ttl
README.md
Actions タブで、GitHub Actionsが正しく動いているか確認します。
以下のように表示されると、アップロードとLambdaの呼び出しが行えていることになります(Lambdaの戻り値は実装により異なります)。
以後、ttl
ファイルが追加・更新されると、S3アップロードとLambdaが呼び出されます。
まとめ
今回 GitHub と AWS を連携させて処理を行ってみました。思ったよりも簡単に設定できることがわかりました。
オープンデータ等、広く外部に公開したいファイルについては、AWSでホスティングするよりもGitHubで公開したほうが扱いやすいと思いますし、将来的に他の方が作られたオープンデータを受け入れるようなことをしたい場合もこの仕組みであれば、そのまま流用して自動化ができると思います。
割とそういった使い方には相性の良い組み合わせかもしれません。
この設定は、以下のWebサイトのために行ったものです。
特定の形式で公開されるオープンデータWeb API(SPARQLエンドポイント)を登録して、検索できるようにしたサイトです。
以下に特徴と使い方をまとめていますので、興味がある方いましたら使ってみてください。
Discussion