Quantcast
Channel: kakakakakku blog
Viewing all 902 articles
Browse latest View live

Amazon Inspector Lambda 標準スキャンで Lambda Layer に含まれる CVE を検出する

$
0
0

Amazon Inspector Lambda 標準スキャンを有効化すると「AWS Lambda 関数」「AWS Lambda Layer」を対象に脆弱性 (CVE) を検出できる🔐 Lambda Layer もサポートしていることを確認してみた👌

docs.aws.amazon.com

requests 2.30.0

今回は Python パッケージの requestsを使う.requests 2.30.0 には Medium レベルの脆弱性 CVE-2023-32681が含まれているため,今回は意図的に requests 2.30.0 を含んだ Lambda Layer を作っておく.AWS SAM を使えば Lambda Layer も簡単に作れる❗️

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:requests2300:Type: AWS::Serverless::LayerVersion
    Properties:LayerName: requests-2-30-0
      ContentUri: requests-2.30.0/
      CompatibleRuntimes:- python3.12
    Metadata:BuildMethod: python3.12

pypi.org

Lambda Layer を追加した動作確認用の AWS Lambda 関数も作っておく.コードは以下のように requests を使って API(今回は random.dog)を呼び出すシンプルな実装にしておく🐕

import requests


deflambda_handler(event, context):
    print(requests.get('https://random.dog/woof.json').json())

動作確認

すると Amazon Inspector で CVE を検出できた❗️

標準スキャンから除外する

ドキュメントに書いてある通り,Amazon Inspector Lambda 標準スキャンの対象になるのは 過去90日間に呼び出された・または更新された AWS Lambda 関数になるため,日常的に実行している AWS Lambda 関数は基本的にすべて対象になる.もし除外する場合は InspectorExclusion: LambdaStandardScanningというタグを付ければ OK👌


GitHub Codespaces で起動した LocalStack に Resource Browser から接続する

$
0
0

GitHub Codespacesで起動した LocalStack に LocalStack Resource Browserから接続できる👌ただし LocalStack ポートを公開するためあくまで一時的な検証用途で使う前提として \( 'ω')/

LocalStack CLI セットアップ

ドキュメントを参考に GitHub Codespaces に LocalStack CLI をセットアップする.

$ curl --output localstack-cli-3.5.0-linux-amd64-onefile.tar.gz \--location https://github.com/localstack/localstack-cli/releases/download/v3.5.0/localstack-cli-3.5.0-linux-amd64-onefile.tar.gz
$ sudo tar xvzf localstack-cli-3.5.0-linux-*-onefile.tar.gz -C /usr/local/bin

docs.localstack.cloud

LocalStack 起動

LocalStack をデフォルトの 4566ポートで起動する.

$ localstack start-d

ポート共有

GitHub Codespaces の 4566ポートを公開する🌍

$ gh codespace ports visibility 4566:public

URL は https://<CODESPACE_NAME>-<PORT>.app.github.devというフォーマットになる.

$ echo https://${CODESPACE_NAME}-4566.app.github.dev
https://xxx-xxxx-xxxxxxxx-xxxxxxxxxxxxxxx-4566.app.github.dev

docs.github.com

LocalStack Resource Browser 設定

LocalStack Resource Browser の Endpoint に取得した URL を設定すると接続できる👌

動作確認

awslocalコマンドをセットアップして,LocalStack 上に Amazon S3 バケット codespaces-sandboxをデプロイする.

$ awslocal s3api create-bucket \--bucket codespaces-sandbox \--create-bucket-configurationLocationConstraint=ap-northeast-1
{"Location": "http://codespaces-sandbox.s3.localhost.localstack.cloud:4566/"}

LocalStack Resource Browser で Amazon S3 バケットを確認できた👌

お掃除

検証が終わったらお掃除をする.ポート共有をプラベートに戻して,LocalStack を停止しておく🛑

$ gh codespace ports visibility 4566:private
$ localstack stop

LocalStack を使って DynamoDB TTL 機能をローカル環境で試す

$
0
0

LocalStack は Amazon DynamoDB TTL (Time To Live) 機能をサポートしている❗️Amazon DynamoDB を実環境にデプロイする前に TTL の動作確認ができて便利👌最近 LocalStack の Amazon DynamoDB TTL 機能を試す機会があって,簡単にまとめておこうと思う〜 \('ω')/ LocalStack の Amazon DynamoDB で TTL 機能がサポートされていることは案外知られていなかったりすると思う.

docs.localstack.cloud

LocalStack の Amazon DynamoDB TTL 機能

LocalStack の Amazon DynamoDB TTL 機能を使うためには大きく2種類の選択肢がある📝

  1. 1時間ごとに削除する
  2. API を実行する

1. 1時間ごとに削除する

1時間ごとに削除する仕組みは Amazon DynamoDB TTL 機能の挙動に似ていて,期限切れになった後に非同期に削除される🗑実際の Amazon DynamoDB TTL 機能はドキュメントに 数日以内に削除と書いてあって,LocalStack だと1時間ごとに削除されるという違いがある.

docs.aws.amazon.com

1時間ごとに削除する場合は環境変数 DYNAMODB_REMOVE_EXPIRED_ITEMS=1を設定して LocalStack を起動する必要がある🚀

$ DYNAMODB_REMOVE_EXPIRED_ITEMS=1 localstack start-d

動作確認のために awslocalコマンドで Amazon DynamoDB テーブルを作る.今回は expired_atを Amazon DynamoDB TTL 機能の対象にする👌

$ awslocal dynamodb create-table \--table-name sandbox-ttl \--attribute-definitionsAttributeName=id,AttributeType=S \--key-schemaAttributeName=id,KeyType=HASH \--billing-mode PAY_PER_REQUEST

$ awslocal dynamodb update-time-to-live \--table-name sandbox-ttl \--time-to-live-specification'Enabled=true, AttributeName=expired_at'

そしてサンプルデータを投入する.今回は expired_at18:00 / 19:00 / 20:00を設定しておく.

$ awslocal dynamodb put-item --table-name sandbox-ttl \--item'{ "id": { "S": "076c35eb-21bb-4f48-aa38-b27e513c0774" }, "title": { "S": "Title A" }, "expired_at": { "N": "1721466000" } }'
$ awslocal dynamodb put-item --table-name sandbox-ttl \--item'{ "id": { "S": "16244eb7-22ff-44c3-8411-17cc270fedf9" }, "title": { "S": "Title B" }, "expired_at": { "N": "1721469600" } }'
$ awslocal dynamodb put-item --table-name sandbox-ttl \--item'{ "id": { "S": "654a47a1-cd1c-42e8-9eff-0e5296442cb8" }, "title": { "S": "Title C" }, "expired_at": { "N": "1721473200" } }'
id title expired_at
076c35eb-21bb-4f48-aa38-b27e513c0774 Title A 1721466000(2024/07/20 18:00:00
16244eb7-22ff-44c3-8411-17cc270fedf9 Title B 1721469600(2024/07/20 19:00:00
654a47a1-cd1c-42e8-9eff-0e5296442cb8 Title C 1721473200(2024/07/20 20:00:00

サンプルデータ投入直後の状態を確認する.

$ awslocal dynamodb scan --table-name sandbox-ttl
{"Items": [{"title": {"S": "Title A"},
            "id": {"S": "076c35eb-21bb-4f48-aa38-b27e513c0774"},
            "expired_at": {"N": "1721466000"}},
        {"title": {"S": "Title C"},
            "id": {"S": "654a47a1-cd1c-42e8-9eff-0e5296442cb8"},
            "expired_at": {"N": "1721473200"}},
        {"title": {"S": "Title B"},
            "id": {"S": "16244eb7-22ff-44c3-8411-17cc270fedf9"},
            "expired_at": {"N": "1721469600"}}],
    "Count": 3,
    "ScannedCount": 3,
    "ConsumedCapacity": null
}

1時間ほど待って,もう一度データを確認する.期待通りに Title Aのデータが消えている👌

$ awslocal dynamodb scan --table-name sandbox-ttl
{"Items": [{"title": {"S": "Title C"},
            "id": {"S": "654a47a1-cd1c-42e8-9eff-0e5296442cb8"},
            "expired_at": {"N": "1721473200"}},
        {"title": {"S": "Title B"},
            "id": {"S": "16244eb7-22ff-44c3-8411-17cc270fedf9"},
            "expired_at": {"N": "1721469600"}}],
    "Count": 2,
    "ScannedCount": 2,
    "ConsumedCapacity": null
}

2. API を実行する

次に API を試す.LocalStack は一時的な動作確認で使うことも多く,個人的は1時間待つよりは API でサッと消せるのが便利だと思う❗️LocalStack には /_aws/dynamodb/expiredという Amazon DynamoDB TTL 機能専用の API エンドポイントがある👌

同じサンプルデータのまま API を実行する.

$ curl -X DELETE localhost:4566/_aws/dynamodb/expired
{"ExpiredItems": 1}

データを確認すると,期待通りに Title Bのデータが消えている👌

$ awslocal dynamodb scan --table-name sandbox-ttl
{"Items": [{"title": {"S": "Title C"},
            "id": {"S": "654a47a1-cd1c-42e8-9eff-0e5296442cb8"},
            "expired_at": {"N": "1721473200"}}],
    "Count": 1,
    "ScannedCount": 1,
    "ConsumedCapacity": null
}

その他

LocalStack ドキュメントを読んでいたらエンドポイントの表記に誤りがあって修正しておいた ✅

すぐに merge してもらえた〜 \( 'ω')/

github.com

AWS Step Functions で AWS Lambda 関数を使わず Amazon S3 にオブジェクトをアップロードする

$
0
0

AWS Step Functions でワークフローを構築しているときに AWS Step Functions の実行時に渡すインプットの一部をファイルとして Amazon S3 に保存したいという場面があったりする❗️実際に最近あった \( 'ω')/

もちろん AWS Lambda 関数を追加すれば柔軟で自由度高くワークフローを拡張できるけど,比較的シンプルな処理であれば無理に AWS Lambda 関数を増やすのではなく,AWS Step Functions の「AWS SDK 統合」を活用する方が良かったりする.ちなみにシンプルな処理であれば AWS Lambda 関数は不要というベストプラクティスは「直接統合 (Direct Integrations)」という表現で AWS Well-Architected Framework Serverless Applications Lensに載っている👌

docs.aws.amazon.com

ワークフロー

今回は動作確認のために AWS SDK 統合 s3:PutObjectのみを設定する.

そして,アップロードする Amazon S3 バケットのオブジェクトキーは AWS Step Functions の実行名とインプットとして渡す nameを組み合わせるため,AWS Step Functions の組み込み関数 States.Formatと AWS Step Functions のコンテキストオブジェクト $$.Execution.Nameを設定する.

docs.aws.amazon.com

docs.aws.amazon.com

今回は AWS CDK でワークフローを実装した.

import{
  Stack,
  StackProps,
  aws_s3,
  aws_stepfunctions,
  aws_stepfunctions_tasks,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass SandboxCdkStepfunctionsPutObjectStack extends Stack {constructor(scope:Construct, id:string, props?:StackProps) {super(scope, id, props)

    const bucket = new aws_s3.Bucket(this, 'Bucket', {bucketName: 'kakakakakku-sandbox-stepfunctions-put-object',
    })

    const putObjectTask = new aws_stepfunctions_tasks.CallAwsService(this, 'PutObjectTask', {service: 's3',
      action: 'putObject',
      parameters: {'Bucket': bucket.bucketName,
        'Key.$': `States.Format('{}/{}', $$.Execution.Name, $.name)`,
        'Body.$': '$.body',
        'ContentType': 'application/json',
      },
      iamResources: ['*'],
    })

    new aws_stepfunctions.StateMachine(this, 'SandboxCdkStepfunctions', {stateMachineName: 'sandbox-cdk-stepfunctions-put-object',
      definitionBody: aws_stepfunctions.DefinitionBody.fromChainable(putObjectTask),
    })
  }}

最終的にデプロイされたワークフロー定義は以下👌

{"StartAt": "PutObjectTask",
  "States": {"PutObjectTask": {"End": true,
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:s3:putObject",
      "Parameters": {"Bucket": "kakakakakku-sandbox-stepfunctions-put-object",
        "Key.$": "States.Format('{}/{}', $$.Execution.Name, $.name)",
        "Body.$": "$.body",
        "ContentType": "application/json"
      }}}}

動作確認

2種類のインプットを準備して,ワークフローを実行する❗️

{"name": "sandbox001.json",
    "body": {"title": "title001",
        "body": "body001"
    }}
{"name": "sandbox002.json",
    "body": {"title": "title002",
        "body": "body002"
    }}

すると,期待通りに Amazon S3 バケットにオブジェクトが追加されている👌

$ aws s3api list-objects --bucket kakakakakku-sandbox-stepfunctions-put-object | jq -r'.Contents[].Key'
f32ea288-3103-4c8a-9b65-6c57298420f8/sandbox002.json
f5f16907-279b-447a-9e1c-eddc44ea78e3/sandbox001.json

そして f5f16907-279b-447a-9e1c-eddc44ea78e3/sandbox001.jsonを開くと,期待通りにインプットの bodyに指定した JSON になっていた👌

{"title":"title001","body":"body001"}

Terraform AWS Provider で AWS Chatbot を構築する

$
0
0

2024年8月2日にリリースされた Terraform AWS Provider v5.61.0で AWS Chatbot の「Slack 設定 (SlackChannelConfiguration)」「Teams 設定 (MicrosoftTeamsChannelConfiguration)」を設定できるようになった❗️

github.com

背景

今までは AWS Provider を使って AWS Chatbot を構築できず,AWS Provider 側の issue は2020年3月から Open していた.

github.com

とは言え AWS Provider は AWS SDK for Goに依存していて,AWS SDK for Go で AWS Chatbot がサポートされていないという点が根本的な問題ではあった.

github.com

そして,2024年2月に AWS SDK for Go が AWS Chatbot をサポートしたため,AWS Provider もサポートできるようになったという背景がある.

github.com

今まで

Slack を例にすると,今までは以下のようなワークアラウンドを選択して AWS Chatbot を構築していたと思う.

仕事では AWS Cloud Control Provider (awscc)を導入していて,過去にブログにもまとめてある📝

kakakakakku.hatenablog.com

Terraform コード

さっそく AWS Provider でサポートされた aws_chatbot_slack_channel_configurationリソースを試す👌 以下のように実装した.IAM Role と Amazon SNS トピックは省略する.

resource"aws_chatbot_slack_channel_configuration""main"{configuration_name = "sandbox"iam_role_arn       = aws_iam_role.chatbot.arn
  slack_team_id      = "XXXXXXXXX"slack_channel_id   = "XXXXXXXXX"sns_topic_arns     = [aws_sns_topic.chatbot.arn]}

簡単だ \( 'ω')/

動作確認

AWS Chatbot の「テストメッセージを送信」と Amazon SNS の「メッセージの発行」を実行して,期待通り Slack に通知された👏

ちなみに Amazon SNS から送信するときは以下のドキュメントを参考にカスタムフォーマットを使った.

{"version": "1.0",
    "source": "custom",
    "content": {"description": ":tada: Terraform AWS Provider v5.61.0 now supports the `aws_chatbot_slack_channel_configuration` resource! yay!"
    }}

docs.aws.amazon.com

補足: SlackWorkspaceIdSlackTeamId

Slack Workspace ID を指定するときに,AWS Cloud Control Provider (awscc) の場合は AWS CloudFormation のリソースに対応しているため SlackWorkspaceIdを設定するけど,AWS Provider の場合は slack_team_idを設定するという違いがあった.ちなみに AWS Chatbot APIのリクエストパラメータは SlackTeamIdになっていた.

docs.aws.amazon.com

docs.aws.amazon.com

ポスト

Zenn Book で「LocalStack 実践入門」を公開しました

$
0
0

今週月曜日(2024年8月5日)に Zenn Book で完全無料の学習コンテンツ「LocalStack 実践入門 | AWS アプリケーション開発ワークショップ」を公開しましたー🎉

AWS エミュレーターの LocalStackに実践的に入門するワークショップです❗️

zenn.dev

概要 🚀

アプリケーションを AWS 上で稼働させていて,マネージドサービスを中心に組み合わせたりすると,ローカル環境での開発がしにくく感じたり,AWS サービスに依存したアプリケーションコードの単体テストがしにくく感じることがあるはずです💡そんなときには LocalStackが便利だぞ〜👏という技術的な選択肢を紹介したく,ワークショップの開発を企画しました.

アプリケーションコードは Python (Boto3)を使っていて,単体テストは pytestを使っています.比較的簡単なコードですし,コード解説も載せているため,Python の経験がなくても大丈夫かなと思います👌

www.localstack.cloud

読者層 🎃

このワークショップは「LocalStack 未経験者」はもちろん「AWS 初学者」にもおすすめです❗️

僕自身は過去に AWS を教える仕事(テクニカルトレーナー)をしていて,現在も AWS 未経験者の多い組織では AWS 導入の支援や人材育成のサポートをしています.そういった経験の中でよく聞くのは「AWS を試しながら学習したいけど課金が怖い(無料利用枠もよくわからない)」という声です.もちろんある程度 AWS に慣れれば,料金ページや無料利用枠を理解しながら「試しながら学ぶ」というサイクルを回せますが,初学者にとっては第一歩を踏み出すこと自体にハードルの高さがあって〜という相談もよく受けます👀

LocalStack を使えば,AWS アカウントを作らずに AWS の学習ができます.macOS などのラップトップ上に「使い捨てできる自分専用の AWS 環境」を構築できるようなイメージです🌍 もちろん完璧なエミュレーターではないですし,サポートしている AWS サービスも限られていますが,それでも学習用途であれば十分かなと思います👌

docs.localstack.cloud

ワークショップ構成 🧪

ワークショップは Chapter.1 から Chapter.7 まであります.Chapter ごとにステップバイステップに進められて,Chapter を進めるごとにアーキテクチャも広がっていくように作っています.そして Chapter.8 は「応援購入」のための付録です.ワークショップに関連する小ネタを紹介しています.次のワークショップを企画するモチベーションにも繋がりますので,よろしければぜひ❗️

  • Chapter.1: ワークショップ環境をセットアップしよう
  • Chapter.2: LocalStack を使ってみよう
  • Chapter.3: Python コードで Amazon SQS と Amazon S3 を操作しよう
  • Chapter.4: AWS CloudFormation でデプロイを自動化しよう
  • Chapter.5: AWS SAM でサーバレスアプリケーションをデプロイしよう
  • Chapter.6: 使い捨ての LocalStack で単体テストを実行しよう
  • Chapter.7: Amazon API Gateway で API をデプロイしよう
  • Chapter.8: 付録(応援購入)

GitHub Codespaces 🌍

今回のワークショップでは「環境構築でつまづいて欲しくないな〜」ということを意識しました.そこでワークショップで使える統一的なオンライン環境として GitHub Codespacesを選んだところは工夫したポイントの一つで,ワークショップを進めやすくなっているはずです💪

github.co.jp

GitHub Codespaces では自動的に Python 3.12・AWS CLI・Docker・GitHub CLI を含んだ環境を起動するようにしています.

{"name": "aws-application-workshop-using-localstack",
    "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
    "features": {"ghcr.io/devcontainers/features/aws-cli:1": {},
        "ghcr.io/devcontainers/features/docker-in-docker:2": {},
        "ghcr.io/devcontainers/features/github-cli:1": {}}}

ちなみに LocalStack・LocalStack AWS CLI・LocalStack AWS SAM CLI も GitHub Codespaces で自動的にセットアップできますが,LocalStack に関連するツールセットを意識することも重要かなと考えて,ワークショップでは意図的にセットアップしてもらうようにしています💡

github.com

GitHub Codespaces を使ったワークショップ(ハンズオン)の実施はとても便利で,今後もっと流行るかもしれないな〜と思っています❗️

Zenn Book 📕

今回のワークショップを企画するときに配信プラットフォームも悩みました.

ワークショップ以外に解説動画を編集して Udemy や YouTube に公開する案は,準備コストが大きすぎることとアップデートがしにくいことを懸念して避けました.また LocalStack に関する書籍を執筆する案も考えて,出版までの道のりが長すぎることと,今回書籍化するほどのボリュームではなかったこともあって避けました💨

最終的に Zenn Bookを選びました.Zenn での執筆は初体験でしたが,Markdown 拡張・プレビュー機能・GitHub 連携など,執筆体験が良かったです❗️さらにコンテンツを基本無料で公開しつつ一部の Chapter のみ有料にしたり,バッジを贈れたり,執筆者のモチベーションに繋がる仕組みが整っているのも良いな〜と思いました.

まとめ 🐸

ぜひ「LocalStack 実践入門 | AWS アプリケーション開発ワークショップ」をお試しください❗️

好評であれば続編も考えたいな〜 \( 'ω')/

zenn.dev

ポスト 🦜

AWS Lambda 関数で Amazon ECR のイメージタグを上書きしたら AWS Lambda 関数を更新する

$
0
0

AWS Lambda 関数をコンテナイメージでデプロイしているときに,latestタグを使っていたり,同じタグを上書きして再利用していたりする場合がある.例えば AWS Well-Architected Framework「Container Build Lens」の設計原則 (Design principles) には「避けるべし!」と書いてあったりするけど,それでも現場ではそういう運用になっていることもある👀

The automatically created latest tag shouldn’t be used. A tagging strategy should produce immutable tags. Teams should avoid overwrite tags because this makes it hard to reproduce issues of specific application versions.

docs.aws.amazon.com

そのときに Amazon ECR のイメージタグを上書きしても AWS Lambda 関数に反映されなくて困ることがある.実は仕様はドキュメントに載っていて,AWS Lambda 関数にデプロイされたコンテナイメージはイメージダイジェストで決まっていて,Amazon ECR のイメージタグを上書きしても AWS Lambda 関数は自動的に更新されないようになっている.よって,意図的に AWS Lambda 関数を更新する必要がある👌

コンテナイメージとして定義された関数の場合、Lambda はイメージタグをイメージダイジェストに転換します。Amazon ECR では、イメージタグを新しいイメージに更新しても、Lambda は関数を自動的に更新しません。

docs.aws.amazon.com

検証

version 1

ドキュメントを参考に AWS Lambda 関数として Python コードを実行するコンテナイメージを作る.

docs.aws.amazon.com

今回は Amazon ECR のイメージタグを上書きして確認できれば良くて,lambda_function.pyは以下のように単純に version 1と出力するだけにした🙏

defhandler(event, context):
    print('version 1')

さらに Dockerfileは以下のようにした.Python ベースイメージを使えば AWS Lambda RIC (Runtime Interface Clients)をセットアップしなくて OK👌

FROM public.ecr.aws/lambda/python:3.12

COPY lambda_function.py ${LAMBDA_TASK_ROOT}

CMD["lambda_function.handler" ]

コンテナイメージをビルドして,検証用の AWS Lambda 関数に latestタグとしてデプロイすると「イメージ URI」sha256:f9b01431b221a63d0360b4742c805997cbea81299264e4658774d2582ad0fc09になっていた💡

AWS Lambda 関数を実行すると,期待通りに version 1とログが出力される📝

version 2

次に lambda_function.pyを以下のように更新して,コンテナイメージをビルドして latestタグを上書きする.

defhandler(event, context):
        print('version 2')

もう一度 AWS Lambda 関数を実行すると,引き続き version 1とログが出力されてしまう😅 そこで aws lambda update-function-codeコマンドを実行して,上書きしたコンテナイメージのイメージダイジェストを指定する必要がある.

$ aws lambda update-function-code \--function-name sandbox \--image-uri000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/sandbox@sha256:10ad544f449ef5f2bb5a0228ffd8b71245f509a8fdc0ba92159c65f2f80fc363

さらにもう一度 AWS Lambda 関数を実行すると,期待通りに version 2とログが出力される📝

まとめ

AWS Lambda 関数をコンテナイメージでデプロイするときは「Amazon ECR のイメージタグを上書きしても AWS Lambda 関数は自動的に更新されないようになっていること」を理解しておく必要がある👌

そして,AWS CodeBuild や GitHub Actions などを使ってコンテナイメージをビルドするときは一緒に aws lambda update-function-codeコマンドを実行する必要がある \( 'ω')/

GitHub Actions の実践的なノウハウが凝縮されている素晴らしい一冊「GitHub CI/CD 実践ガイド」を読んだ

$
0
0

GitHub Actions の実践的なノウハウが凝縮されている一冊「GitHub CI/CD 実践ガイド」を読んだ📕

本書ではソフトウェア開発ライフサイクルから GitHub Actions 基礎トピック・GitHub Actions 実践トピックが紹介されていて,さらに GitHub Actions を活用して実現するリリース自動化・パッケージ管理・セキュリティのシフトレフトまでもカバーされている❗️素晴らしい👏 GitHub Actions をなんとなーく使っていたり,いつも既存のワークフローをコピーしていたりする人は必読かなと \( 'ω')/

また著者の経験に基づくベストプラクティス(こうすると良いよ〜的な)が散りばめられているのも現場目線で読めて良かった❗️

もちろん機能面に関しては GitHub Actions のドキュメントを読めば書いてあることも多いけど,すべてを読むのは大変だし,微妙に読みにくかったりもするし,書籍として体系的にまとまっていることはとても価値があると思う💡

docs.github.com

目次 🧩

基礎編

  • 第1章: ソフトウェア開発と GitHub
  • 第2章: GitHub Actions の基礎概念
  • 第3章: ワークフロー構文の基礎
  • 第4章: 継続的インテグレーションの実践
  • 第5章: 運用しやすいワークフローの設計
  • 第6章: アクションによるモジュール化

実践編

  • 第7章: クリーンなリポジトリの維持
  • 第8章: Dependabot による依存関係バージョンアップ
  • 第9章: GitHub Releases によるリリース自動化
  • 第10章: GitHub Packages によるパッケージ管理
  • 第11章: OpenID Connect によるセキュアなクラウド連携
  • 第12章: コンテナオーケストレーションのデプロイメント
  • 第13章: アクションのオープンソース化

応用編

  • 第14章: GitHub Actions の高度な使い方
  • 第15章: GitHub Actions のセキュリティ
  • 第16章: セキュリティのシフトレフト
  • 第17章: GitHub Apps トークンによるクロスリポジトリアクセス
  • 第18章: 継続的デリバリーの実践

あくまで個人的な感想だけど「第6章 (composite action)」「第14章 (reusable workflow)」は内容的に続いていても良いかな〜とは思った.

gihyo.jp

新たな発見もあった 🧩

僕自身は比較的 GitHub Actions の経験があって,復習も兼ねてザーッと読んだけど,それでも「知らなかったぞ〜💡」という情報も載っていて新たな発見もあった❗️具体的には以下など.

  • 中間環境変数テクニック(第3章)
  • Secrets のログマスクは回避できてしまう(第3章)
  • ログをグループ化して可読性を高める(第5章)
  • CODEOWNERS ファイル(第7章)
  • スクリプトインジェクション(第15章)
  • Gitleaks(第16章)

NAMEkakakakakkuと設定して,Secrets のログマスクを回避する記法を試してみた.なるほどなぁー😅

jobs:secrets:runs-on: ubuntu-latest
    env:NAME: ${{ secrets.NAME }}
    steps:- run: echo ${NAME:0:1} ${NAME#?}

::group::{title}という記法でログをグループ化する Tips も知らなくて,実際に試してみた👌仕事でちょうど使えそうな部分があったからさっそく導入してみたりもした❗️

docs.github.com

jobs:grouping-log-lines:runs-on: ubuntu-latest
    steps:- uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:python-version:'3.12'- name: Setup packages
        run: |
          echo '::group::pip install logs'
          pip install -r requirements.txt
          echo '::endgroup::'

GitHub Apps 解説

第17章「GitHub Apps トークンによるクロスリポジトリアクセス」に載っているクレデンシャルと GitHub Apps の解説は貴重だった❗️曖昧だった理解を整理できた.さらに classic Personal Access Tokens (PAT) は「最悪の選択肢」と書いてあって「それな〜😱」という気持ちに.Fine-grained Personal Access Tokens (PAT) に有効期限があるのは良いとしても,今もまだ Betaって書いてあって使いたくない気持ちがある💨

docs.github.com

その他読書メモ

第2章に workflow_dispatchの紹介が載っていて良かった.意外と知られてなくて無理矢理コミットしてトリガーしてる人をよく見る.あと本書には載ってなかったけど,workflow_dispatchはワークフローファイルがデフォルトブランチに存在してる前提で実行できるから,最初プルリクエストを送った段階だと試せなくてハマるという経験があったりする📝

docs.github.com

第2章の最後に「課金モデル」の解説が含まれているのも良かった💰同じく結構知られてない気がする(個人的な観測範囲内では).

docs.github.com

第4章で紹介されてた GitHub Actions の「concurrency 設定」は知っておくと便利❗️前にブログを書いた.

kakakakakku.hatenablog.com

もしかしたら「act」の紹介もあるかな〜と思って読み進めていたけど,本書には出ていなかったはず👀

github.com

kakakakakku.hatenablog.com

まとめ

GitHub Actions を使ってるチームは多いと思うけど,もっと GitHub Actions を知ることでさらに便利になって活用が進むと思う.本書には GitHub Actions の実践的なノウハウが凝縮されていて素晴らしい一冊だった📕 おすすめ \( 'ω')/

著者のブログ記事・本書のコードリポジトリもリンクしておくー🔗

nekopunch.hatenablog.com

github.com

ポスト


フォルダオブジェクトの有無によって aws s3api list-objects-v2 コマンドの結果が異なる

$
0
0

AWS CLI で Amazon S3 の aws s3api list-objects-v2コマンドを使ってオブジェクト数をカウントしてたときに少しハマったことがあって簡単にメモしておこうと思う👌

awscli.amazonaws.com

起きたこと

例えば Amazon S3 バケットに folder/file.txtというオブジェクトが保存されているときに aws s3api list-objects-v2コマンドを実行すると「オブジェクト数1」の場合と「オブジェクト数2」の場合があって,オブジェクト数をカウントするコードが期待通りに動かなかったという場面があった👀(検証用の Amazon S3 バケットは削除済)

オブジェクト数1

Key: folder/file.txt

オブジェクト数2

Key: folder/
Key: folder/file.txt

結論

結論としては「フォルダオブジェクト」の有無によって結果が異なっていた.よって,aws s3api list-objects-v2コマンドを使ってオブジェクト数をカウントするときはフォルダオブジェクトの有無を考慮すると良さそう.アプリケーション実装によっても異なると思う.

ちなみに Amazon S3 には厳密には「フォルダ」という概念はなく,キーの末尾が /となる 0 Byte のオブジェクトがあるとマネジメントコンソールではフォルダとして表示してくれるという前提がある👌以下のドキュメントに詳しく載っている.

docs.aws.amazon.com

検証1: オブジェクト数1

オブジェクト数: 0

$ aws s3api list-objects-v2 \--bucket kakakakakku-sandbox-list-objects-v2
{"RequestCharged": null
}

オブジェクト folder/file.txtを追加する.

$ aws s3api put-object \--bucket kakakakakku-sandbox-list-objects-v2 \--key folder/file.txt \--body file.txt

オブジェクト数: 1(LastModifiedの値は修正している)

$ aws s3api list-objects-v2 \--bucket kakakakakku-sandbox-list-objects-v2 \--prefix folder/
{"Contents": [{"Key": "folder/file.txt",
            "LastModified": "2024-07-30T00:00:00+00:00",
            "ETag": "\"4c21212908803d3bf45f5a162bed13f1\"",
            "Size": 27,
            "StorageClass": "STANDARD"}],
    "RequestCharged": null
}

検証2: オブジェクト数2

オブジェクト数: 0

$ aws s3api list-objects-v2 \--bucket kakakakakku-sandbox-list-objects-v2
{"RequestCharged": null
}

フォルダオブジェクト folder/を追加する.

$ aws s3api put-object \--bucket kakakakakku-sandbox-list-objects-v2 \--key folder/

オブジェクト数: 1(LastModifiedの値は修正している)

$ aws s3api list-objects-v2 \--bucket kakakakakku-sandbox-list-objects-v2 \--prefix folder/
{"Contents": [{"Key": "folder/",
            "LastModified": "2024-07-30T00:00:00+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "Size": 0,
            "StorageClass": "STANDARD"}],
    "RequestCharged": null
}

オブジェクト folder/file.txtを追加する.

$ aws s3api put-object \--bucket kakakakakku-sandbox-list-objects-v2 \--key folder/file.txt \--body file.txt

オブジェクト数: 2(LastModifiedの値は修正している)

$ aws s3api list-objects-v2 \--bucket kakakakakku-sandbox-list-objects-v2 \--prefix folder/
{"Contents": [{"Key": "folder/",
            "LastModified": "2024-07-30T00:00:00+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\"",
            "Size": 0,
            "StorageClass": "STANDARD"},
        {"Key": "folder/file.txt",
            "LastModified": "2024-07-30T00:00:00+00:00",
            "ETag": "\"4c21212908803d3bf45f5a162bed13f1\"",
            "Size": 27,
            "StorageClass": "STANDARD"}],
    "RequestCharged": null
}

Lambda 関数(コンテナ)をテストできる testcontainers-python v4.8.0 の新機能「AWSLambdaContainer」

$
0
0

2024年8月14日にリリースされた testcontainers-python v4.8.0の新機能を確認していたら new: Added AWS Lambda moduleと書いてあって,これは何だろう〜と気になって試してみた❗️

github.com

簡単に言えば,テスト実行時に testcontainers-python で AWS Lambda 関数(コンテナ)を起動して,AWS Lambda RIE (Runtime Interface Emulator)エンドポイント /2015-03-31/functions/function/invocationsを呼び出したレスポンスを assert できる機能だった💡AWS Lambda 関数(コンテナ)の振る舞いをデプロイする前にテストできる \( 'ω')/

さっそく AWSLambdaContainer を試す

ディレクトリ構成

.
├── Dockerfile
├── requirements-test.txt
├── src
│   └── app.py
└── tests
    └── test_app.py

👾 Dockerfile

まず Dockerfileを作る.Python ベースイメージを使えば AWS Lambda RIC (Runtime Interface Clients)AWS Lambda RIE (Runtime Interface Emulator)をセットアップしなくて OK👌 Ubuntu などをベースイメージにする場合は別途セットアップする必要がある.

docs.aws.amazon.com

FROM public.ecr.aws/lambda/python:3.12

COPY src/app.py ${LAMBDA_TASK_ROOT}

CMD["app.handler" ]

👾 src/app.py

AWS Lambda 関数で実行するコードはサンプルとして受け取った eventをそのまま返す実装にした.

defhandler(event, context):
    return event

👾 tests/test_app.py

以下のドキュメントを参考にしつつテストコードを書いてみた❗️

testcontainers-python.readthedocs.io

まず DockerImageDockerfileからコンテナイメージをビルドしつつ,AWSLambdaContainerで AWS Lambda 関数(コンテナ)を起動する💡そして send_request()で AWS Lambda RIE (Runtime Interface Emulator) のエンドポイント /2015-03-31/functions/function/invocationsを呼び出す.

今回は4種類の assertを実装した👌

  • エンドポイント
  • ステータスコード
  • レスポンス (title)
  • レスポンス (url)
import re

from testcontainers.aws import AWSLambdaContainer
from testcontainers.core.image import DockerImage


deftest_function():
    with DockerImage(path='.') as image:
        with AWSLambdaContainer(image=image, port=8080) as func:
            pattern = r'http://localhost:\d+/2015-03-31/functions/function/invocations'assert re.match(pattern, func.get_api_url())

            response = func.send_request(
                {
                    'title': 'kakakakakku blog',
                    'url': 'https://kakakakakku.hatenablog.com/',
                }
            )
            body = response.json()

            assert response.status_code == 200assert body['title'] == 'kakakakakku blog'assert body['url'] == 'https://kakakakakku.hatenablog.com/'

ちなみに get_api_url()という関数はドキュメントには載ってなかったけど,実装を確認しているときに発見した.ポート部分は毎回変わるけど http://localhost:61522/2015-03-31/functions/function/invocationsという値が返ってくる😀 まさに RIE エンドポイント \( 'ω')/

github.com

👾 requirements-test.txt

AWSLambdaContainerの依存する ServerContainerは HTTP リクエストを操作するライブラリとして内部的に HTTPXを使っているようだった.

www.python-httpx.org

よって,AWSLambdaContainerのレスポンスは HTTPX オブジェクトになるため,セットアップを忘れると ModuleNotFoundError: No module named 'httpx'というエラーが出る.今回 requirements-test.txtは以下のようにした.

httpx==0.27.0
pytest==8.3.2
testcontainers==4.8.0

✅ 動作確認

OK👌

$ python -m pytest --verbose

tests/test_app.py::test_function PASSED                                                                                                                                     [100%]

まとめ

testcontainers-python v4.8.0の新機能「AWSLambdaContainer」を試してみた❗️

ちなみに個人的には AWS Lambda 関数を実装するときは handler()main()を分割して,main()をテストしている.ドキュメントにもベストプラクティス「Lambda ハンドラーをコアロジックから分離します」と紹介されていたりする📝

docs.aws.amazon.com

今回の AWSLambdaContainerでは AWS Lambda RIE (Runtime Interface Emulator)を使って AWS Lambda 関数(コンテナ)の振る舞いをテストできるため,あくまで個人的には単体テストとしてではなく統合テストとして活用できそうかなと思った.

関連記事

kakakakakku.hatenablog.com

Auth0 Terraform Provider: Auth0 の設定を Terraform で管理しよう

$
0
0

Auth0 Terraform Providerを使うと Auth0 の設定を Terraform で宣言的に管理できる.もちろん Terraform の仕組みに沿って terraform planコマンドで確認してから terraform applyコマンドでデプロイできる❗️Auth0 Terraform Provider を試したログをまとめる.今回は「ゼロから実装」「既存リソースをインポートして実装」を試した \( 'ω')/

github.com

Auth0 Deploy CLI

Auth0 の設定を自動デプロイするときの選択肢として,Auth0 Terraform Provider 以外に YAML ベースの Auth0 Deploy CLIもある💡 a0deployコマンドを使って Auth0 の設定をエクスポート・インポートできて,Keyword Replacement 機能を使えば YAML ファイルに環境変数を挿入できる拡張性もある.Auth0 Terraform Provider と Auth0 Deploy CLI の比較は以下に載っている📝

auth0.com

ドキュメントにも載っている通り,普段から Terraform を使っているなら Auth0 Terraform Provider を採用するのが良さそう.Auth0 のために Terraform 環境をゼロから構築するのは学習コストも含めて大変さはあるとは思うけど,Auth0 Deploy CLI には terraform planコマンドに相当するドライラン機能(変更検出)がなく,ドライランのために Auth0 Terraform Provider を採用するという意思決定もあると思う.ちなみに Auth0 Deploy CLI のドライラン機能は2018年から解決されてなく厳しそう💨

github.com

Auth0 Terraform Provider を試す(ゼロから実装)

さっそく Quickstartドキュメントを参考に Auth0 Terraform Provider を試す❗️

1. M2M Application を追加する

まずは Auth0 を操作するための M2M (Machine-to-Machine) Application を Auth0 コンソールで追加する.今回は Auth0 Terraform Providerという名前にした.そして Domain / Client ID / Client Secretを取得して環境変数に設定しておく.

$ exportAUTH0_DOMAIN=''
$ exportAUTH0_CLIENT_ID=''
$ exportAUTH0_CLIENT_SECRET=''

2. Application: terraform init コマンドを実行する

providers.tfを実装して Auth0 Terraform Providerを取得する.

👾 providers.tf

terraform{required_providers{auth0 = {source  = "auth0/auth0"version = ">= 1.4.0"}}}provider"auth0"{}

3. Application: terraform plan コマンドを実行する

次に clients.tfを実装する.まずは Auth0 Application を auth0_clientリソースを使って追加する.設定は必要最低限にして,今回は SPAタイプで,URL 関連は http://localhost:3000にした👌後ほど動作確認で使う.

👾 clients.tf

resource"auth0_client""sample"{name                = "ReactSamples"app_type            = "spa"callbacks           = ["http://localhost:3000"]allowed_origins     = ["http://localhost:3000"]allowed_logout_urls = ["http://localhost:3000"]web_origins         = ["http://localhost:3000"]}

そして terraform planコマンドを実行する.Auth0 Deploy CLIに慣れていると「plan できるの最高じゃん」という気持ちになる😀 実際にオペレーションに自信が持てるようになる.

$ terraform plan
Terraform will perform the following actions:

  # auth0_client.sample will be created
  + resource "auth0_client""sample"{
      + allowed_logout_urls                 =[
          + "http://localhost:3000",
        ]
      + allowed_origins                     =[
          + "http://localhost:3000",
        ]
      + app_type                            ="spa"
      + callbacks                           =[
          + "http://localhost:3000",
        ]
      + client_id                           =(known after apply)
      + custom_login_page_on                =(known after apply)
      + grant_types                         =(known after apply)
      + id                                  =(known after apply)
      + is_first_party                      =(known after apply)
      + is_token_endpoint_ip_header_trusted =(known after apply)
      + name                                ="ReactSamples"
      + oidc_conformant                     =(known after apply)
      + signing_keys                        =(sensitive value)
      + web_origins                         =[
          + "http://localhost:3000",
        ]}

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

4. Application: terraform apply コマンドを実行する

最後は terraform applyコマンドを実行する.

$ terraform apply

auth0_client.sample: Creating...
auth0_client.sample: Creation complete after 0s [id=xxxxx]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

できた❗️最高 \( 'ω')/

ReactSamples Application を追加できた

5. API: terraform plan コマンドと terraform apply コマンドを実行する

今度は resource-servers.tfを実装する.Auth0 API を auth0_resource_serverリソースを使って追加する.設定は必要最低限にした👌

👾 resource-servers.tf

resource"auth0_resource_server""sample"{name        = "ReactSamples"identifier  = "https://react.samples.com"signing_alg = "RS256"}

同じく terraform planコマンドと terraform applyコマンドを実行する.

$ terraform plan
Terraform will perform the following actions:

  # auth0_resource_server.sample will be created
  + resource "auth0_resource_server""sample"{
      + enforce_policies                                =(known after apply)
      + id                                              =(known after apply)
      + identifier                                      ="https://react.samples.com"
      + name                                            ="ReactSamples"
      + signing_alg                                     ="RS256"
      + signing_secret                                  =(known after apply)
      + skip_consent_for_verifiable_first_party_clients =(known after apply)
      + token_dialect                                   =(known after apply)
      + token_lifetime                                  =(known after apply)
      + token_lifetime_for_web                          =(known after apply)}

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

$ terraform apply
auth0_resource_server.sample: Creating...
auth0_resource_server.sample: Creation complete after 1s [id=xxxxx]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

おー \( 'ω')/

ReactSamples API を追加できた

6. 動作確認をする

追加した Auth0 Application と Auth0 API を使って動作確認をする.今回はサクッと試せる Auth0 React Samplesを使う.

github.com

フロントエンドを起動して,Log inボタンを押すと Auth0 の認証画面が出て,Sign upボタンからサインアップするとログインできた❗️Auth0 Terraform Provider で追加したリソースを期待通りに使えた.

Auth0 React Samples: 初期画面

Auth0 React Samples: ログイン画面

Auth0 React Samples: ログイン後

7. Google 認証を無効化する

デフォルトでは Google 認証が有効化されていて,ログイン画面に Continue with Googleが表示されている.今度は Auth0 Terraform Provider で Google 認証を無効化する.

さっそく connections.tfを実装する.Auth0 Terraform Provider には auth0_connection_clientリソースと auth0_connection_clientsリソースがあって,併用はしないようにとドキュメントに書かれていた.今回は auth0_connectionデータソースと auth0_connection_client リソースを使って Auth0 Application と Auth0 Connection を紐付ける🔗

ただし Auth0 Connection は自動的に設定されてしまっていて,一度 Terraform リソースを作ってから削除してみた😇(もしくはリソースをインポートする案もあると思う)

👾 connections.tf

connection_idUsername-Password-AuthenticationIdentifiergoogle-oauth2Identifierを Auth0 コンソールから取得しておく.

data"auth0_connection""username_password_authentication"{connection_id = "con_xxxxx"}data"auth0_connection""google_oauth2"{connection_id = "con_xxxxx"}resource"auth0_connection_client""sample_username_password_authentication"{client_id     = auth0_client.sample.id
  connection_id = data.auth0_connection.username_password_authentication.connection_id
}resource"auth0_connection_client""sample_google_oauth2"{client_id     = auth0_client.sample.id
  connection_id = data.auth0_connection.google_oauth2.connection_id
}

terraform planコマンドと terraform applyコマンドを実行する.

$ terraform plan
Terraform will perform the following actions:

  # auth0_connection_client.sample_google_oauth2 will be created
  + resource "auth0_connection_client""sample_google_oauth2"{
      + client_id     ="xxxxx"
      + connection_id ="con_xxxxx"
      + id            =(known after apply)
      + name          =(known after apply)
      + strategy      =(known after apply)}# auth0_connection_client.sample_username_password_authentication will be created
  + resource "auth0_connection_client""sample_username_password_authentication"{
      + client_id     ="xxxxx"
      + connection_id ="con_xxxxx"
      + id            =(known after apply)
      + name          =(known after apply)
      + strategy      =(known after apply)}

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

$ terraform apply
auth0_connection_client.sample_google_oauth2: Creating...
auth0_connection_client.sample_username_password_authentication: Creating...
auth0_connection_client.sample_google_oauth2: Creation complete after 0s [id=con_xxxxx::xxxxx]
auth0_connection_client.sample_username_password_authentication: Creation complete after 0s [id=con_xxxxx::xxxxx]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

次に無効化する Google 認証を削除する(今回はコメントアウトにした).

👾 connections.tf

# resource "auth0_connection_client" "sample_google_oauth2" {#   client_id     = auth0_client.sample.id#   connection_id = data.auth0_connection.google_oauth2.connection_id# }

もう一度 terraform planコマンドと terraform applyコマンドを実行すると Google 認証を削除できた❗️

$ terraform plan
Terraform will perform the following actions:

  # auth0_connection_client.sample_google_oauth2 will be destroyed# (because auth0_connection_client.sample_google_oauth2 is not in configuration)
  - resource "auth0_connection_client""sample_google_oauth2"{
      - client_id     ="xxxxx" -> null
      - connection_id ="con_xxxxx" -> null
      - id            ="con_xxxxx::xxxxx" -> null
      - name          ="google-oauth2" -> null
      - strategy      ="google-oauth2" -> null
    }

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

$ terraform apply
auth0_connection_client.sample_google_oauth2: Destroying... [id=con_xxxxx::xxxxx]
auth0_connection_client.sample_google_oauth2: Destruction complete after 0s

Apply complete! Resources: 0 added, 0 changed, 1 destroyed.

Google 認証を削除できた

Auth0 React Samples: ログイン画面(Google 認証なし)

イイ感じ❗️

Auth0 Terraform Provider を試す(既存リソースをインポートして実装)

現状まだ experimental(実験的機能)ではあるけど,Auth0 の既存設定を Terraform の importブロックとしてエクスポートする機能がある👀 さっそく Auto-generating Terraform config from Auth0 tenantドキュメントを参考に試す❗️

1. GenerateSamples Application を追加する

インポート対象として Auth0 コンソールで Auth0 Application を追加しておく❗️

GenerateSamples Application を追加した

2. Auth0 CLI をセットアップする

ドキュメントを参考に Auth0 CLI(auth0コマンド)をセットアップする.

$ auth0 --version
auth0 version 1.4.0 54e9a30eeb58a4a7e40e04dc19af6869036bfb32

auth0.github.io

3. auth0 tf generate コマンドを実行する

そして auth0 tf generateコマンドを実行すると,指定した tmp-auth0-tfディレクトリに auth0_generated.tfauth0_import.tfなどがエクスポートされる👌 auth0_generated.tfファイルにはリソース実装が含まれていて,auth0_import.tfには importブロックが含まれている.対象のリソースが多くコード量はそこそこ多かった.

$ auth0 tf generate --output-dir tmp-auth0-tf
Fetching data from Auth0... done
Generating Terraform configuration... done

4. 既存リソースを Terraform 管理にする

エクスポートされた auth0_generated.tfauth0_import.tfなどをそのままデプロイして紐付けることもできるけど,今回は GenerateSamples Application の importブロックとリソース実装を抜き出して clients.tfに追加してみた.

👾 clients.tf

import{id = "xxxxx"to = auth0_client.generatesamples
}resource"auth0_client""generatesamples"{allowed_clients                       = []allowed_logout_urls                   = ["http://localhost:3000"]allowed_origins                       = ["http://localhost:3000"]app_type                              = "spa"callbacks                             = ["http://localhost:3000"]client_aliases                        = []client_metadata                       = {}cross_origin_auth                     = falsecross_origin_loc                      = nullcustom_login_page                     = nullcustom_login_page_on                  = truedescription                           = nullencryption_key                        = {}form_template                         = nullgrant_types                           = ["authorization_code", "implicit", "refresh_token"]initiate_login_uri                    = nullis_first_party                        = trueis_token_endpoint_ip_header_trusted   = falselogo_uri                              = nullname                                  = "GenerateSamples"oidc_backchannel_logout_urls          = []oidc_conformant                       = trueorganization_require_behavior         = nullorganization_usage                    = nullrequire_pushed_authorization_requests = falsesso                                   = falsesso_disabled                          = falseweb_origins                           = ["http://localhost:3000"]jwt_configuration{alg                 = "RS256"lifetime_in_seconds = 36000scopes              = {}secret_encoded      = false}native_social_login{apple{enabled = false}facebook{enabled = false}}refresh_token{expiration_type              = "expiring"idle_token_lifetime          = 1296000infinite_idle_token_lifetime = falseinfinite_token_lifetime      = falseleeway                       = 0rotation_type                = "rotating"token_lifetime               = 2592000}}

5. terraform plan コマンドと terraform apply コマンドを実行する

そして terraform planコマンドと terraform applyコマンドを実行して既存リソースをインポートできた👌

$ terraform plan
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.

$ terraform apply
auth0_client.generatesamples: Importing... [id=xxxxx]
auth0_client.generatesamples: Import complete[id=xxxxx]

Apply complete! Resources: 1 imported, 0 added, 0 changed, 0 destroyed.

6. 既存リソースを更新する

インポートしたリソースの更新を試すため,GenerateSamples Application の descriptionを更新した.ちなみに encryption_keyはエクスポートした状態だと Payload validation error: 'Too few properties defined (0), minimum 1' on property encryption_key (The client's encryption key).というエラーになったため,行ごと削除した.

👾 clients.tf

-  description                           = ""-  encryption_key                        = {}+  description                           = "This is an application to try the `auth0 tf generate` command"

terraform planコマンドと terraform applyコマンドを実行すると descriptionを更新できた❗️

$ terraform plan
Terraform will perform the following actions:

  # auth0_client.generatesamples will be updated in-place
  ~ resource "auth0_client""generatesamples"{
      + description                           ="This is an application to try the `auth0 tf generate` command"
        id                                    ="xxxxx"
        name                                  ="GenerateSamples"# (21 unchanged attributes hidden)# (3 unchanged blocks hidden)}

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

$ terraform apply
auth0_client.generatesamples: Modifying... [id=xxxxx]
auth0_client.generatesamples: Modifications complete after 0s [id=xxxxx]

description を更新できた

まとめ

Auth0 Terraform Providerを使って Auth0 の設定を Terraform で宣言的に管理しよう❗️

AWS CDK で Amazon API Gateway にカスタムドメインを設定する

$
0
0

AWS CDK で Amazon API Gateway に Amazon Route 53 のカスタムドメインを設定してみた💡

実際に試したログをまとめておく \( 'ω')/

サンプルコード

今回は Amazon Route 53 でドメインを取得してある前提とする.サンプルコード上では xxxxx.comにしておく📝

👾 api-gateway-custom-domain.ts

import{
    Stack,
    StackProps,
    aws_apigateway,
    aws_certificatemanager,
    aws_route53,
    aws_route53_targets,
}from'aws-cdk-lib'import{ Construct }from'constructs'exportclass ApiGatewayCustomDomainStack extends Stack {constructor(scope:Construct, id:string, props?:StackProps) {super(scope, id, props)

        const hostedZone = aws_route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {hostedZoneId: 'xxxxx',
            zoneName: 'xxxxx.com',
        })

        const certificate = new aws_certificatemanager.Certificate(this, 'Certificate', {domainName: 'api.xxxxx.com',
            validation: aws_certificatemanager.CertificateValidation.fromDns(hostedZone),
        })

        const api = new aws_apigateway.RestApi(this, 'ApiGateway', {restApiName: 'sandbox-api-gateway-custom-domain',
            domainName: {domainName: 'api.xxxxx.com',
                certificate: certificate,
            },
            disableExecuteApiEndpoint: true,
        })

        api.root.addMethod('GET', new aws_apigateway.HttpIntegration('https://dog.ceo/api/breeds/image/random'))

        new aws_route53.ARecord(this, 'ARecod', {zone: hostedZone,
            recordName: 'api',
            target: aws_route53.RecordTarget.fromAlias(new aws_route53_targets.ApiGateway(api)),
        })
    }}

ポイント

AWS Certificate Manager

AWS Certificate Manager で証明書を取得するために aws_certificatemanager.Certificateを使う.そして今回は Amazon Route 53 で取得したドメインを使うため,DNS 検証で使う CNAME レコードも自動的に作ってくれて便利👌 ちなみに aws_certificatemanager.DnsValidatedCertificateは既に Deprecated になっているので注意🚨

docs.aws.amazon.com

Amazon API Gateway

addDomainName()でカスタムドメインの設定を詳細に実装することもできるけど,今回はシンプルに aws_apigateway.RestApidomainNameを使った.さらに disableExecuteApiEndpointも設定しておくと Amazon API Gateway のデフォルトドメインのアクセスを拒否できる👌

docs.aws.amazon.com

Amazon API Gateway にカスタムドメインを設定するときの解説は aws_apigatewayモジュールの Overview にも詳しく載っているからあわせて見ておくと良いかと❗️

docs.aws.amazon.com

Amazon Route 53

最後は aws_route53.ARecordを使って Amazon API Gateway に対して A レコード(エイリアスレコード)を設定する.デフォルトだと Apex ドメインになるため,今回は recordNameを設定して apiサブドメインにした.

docs.aws.amazon.com

デプロイ

さっそく cdk diffcdk deployを実行する❗️証明書の DNS 検証待ちもあって多少デプロイ時間は長かった.

$ cdk diff ApiGatewayCustomDomainStack

Resources
[+] AWS::CertificateManager::Certificate Certificate Certificate4E7ABB08 
[+] AWS::ApiGateway::RestApi ApiGateway ApiGateway11E7F47B 
[+] AWS::ApiGateway::Deployment ApiGateway/Deployment ApiGatewayDeploymentA26796E824a5989fccba92ddf3192941a8d88d82 
[+] AWS::ApiGateway::Stage ApiGateway/DeploymentStage.prod ApiGatewayDeploymentStageprod1C6D5CD6 
[+] AWS::ApiGateway::DomainName ApiGateway/CustomDomain ApiGatewayCustomDomainDD503C48 
[+] AWS::ApiGateway::BasePathMapping ApiGateway/CustomDomain/Map:--=>ApiGatewayCustomDomainStackApiGateway93CA03C8 ApiGatewayCustomDomainMapApiGatewayCustomDomainStackApiGateway93CA03C8B11D3EE6 
[+] AWS::ApiGateway::Method ApiGateway/Default/GET ApiGatewayGET25EBFEA3 
[+] AWS::Route53::RecordSet ARecod ARecod5038ED2E 

$ cdk deploy ApiGatewayCustomDomainStack

✨  Synthesis time: 2.98s
✨  Deployment time: 88.42s
✨  Total time: 91.4s

動作確認

今回は Amazon API Gateway の「HTTP 統合」を使って Dog APIを呼び出すようにしている.実際に試したところ,カスタムドメインで Amazon API Gateway を呼び出すことができた❗️さらにデフォルトドメインだと Forbiddenになるのを確認できた.

$ curl -s https://api.xxxxx.com | jq .
{"message": "https://images.dog.ceo/breeds/hound-blood/n02088466_9237.jpg",
  "status": "success"}

$ curl -s https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/ | jq .
{"message": "Forbidden"}

関連ドキュメント

docs.aws.amazon.com

docs.aws.amazon.com

読んだ本を振り返る(2024年1-8月)

$
0
0

2024年に読んだ本を年末の振り返りでまとめようと思っていたけど,2024年2月から「毎日10分間読書」という習慣化を始めたことをキッカケにコツコツ読み進められるようになった.中途半端な時期ではあるけど,2024年8月までに読んだ本とその感想(書評記事 or X ポスト)をまとめておこうと思う📝

8ヶ月で「計26冊」読めた❗️技術的な本と仕事で必要なドメイン知識を獲得する本を中心に読んでいた.もともと本を読むのが遅いのにブログに書評記事を書くまでをセットに考えてしまっていて全然進まなかった過去があるけど,2023年から無理に書評記事を書かずに X ポストでも OK という運用に変えて身軽になったのが良かったと思う.

📕 2024年1月: 0冊😇

まったく読めなかったことを反省して,2024年2月12日から「毎日10分間読書」を始めた📅

📕 2024年2月: 2冊😃

現場のプロがわかりやすく教える位置情報エンジニア養成講座

kakakakakku.hatenablog.com

AWS コスト最適化ガイドブック

📕 2024年3月: 1冊😃

Good Code, Bad Code

📕 2024年4月: 3冊😃

Web API テスト技法

テスト駆動 Python 第2版

kakakakakku.hatenablog.com

データエンジニアリングの基礎

kakakakakku.hatenablog.com

📕 2024年5月: 4冊😃

Terraform の教科書

kakakakakku.hatenablog.com

AWS コンピュータービジョン開発の教科書

kakakakakku.hatenablog.com

組織を変える5つの対話

Team Guide to Software Testability

kakakakakku.hatenablog.com

📕 2024年6月: 3冊😃

ソフトウェアアーキテクチャメトリクス

スモール・リーダーシップ

認証と認可 Keycloak 入門

📕 2024年7月: 6冊😃

農業のしくみとビジネスがこれ1冊でしっかりわかる教科書

スマート農業のきほん

エンジニアリングが好きな私たちのためのエンジニアリングマネジャー入門

kakakakakku.hatenablog.com

図解よくわかるスマート農業

データ農業が日本を救う

農業と環境調査のためのリモートセンシング・GIS・GPS 活用ガイド

📕 2024年8月: 7冊😃

図解でよくわかる 病害虫のきほん

ランサムウエアから会社を守る

何もしない習慣

GitHub CI/CD 実践ガイド

kakakakakku.hatenablog.com

エンジニア組織を強くする開発生産性の教科書

朝イチの「ひとり時間」が人生を変える

AWS クラウドネイティブデザインパターン

LocalStack v3.7 の新機能!Amazon S3 の conditional writes(条件付き書き込み)を試す

$
0
0

2024年8月20日に Amazon S3 の conditional writes(条件付き書き込み)がサポートされた💡オブジェクトの上書きを防ぐ仕組みを独自実装せず,Amazon S3 に任せられるようになるところがメリットだと思う.

aws.amazon.com

試す

さっそく PutObjectを試す.AWS CLI だと --if-none-matchオプションを指定すれば OK👌

aws s3api put-objectコマンドの2回目の実行で PreconditionFailedエラーが出ることを確認できた.

$ echo'hello'> hello.txt

$ BUCKET=kakakakakku-sandbox-conditional-writes
$ aws s3api create-bucket \--bucket${BUCKET}\--create-bucket-configurationLocationConstraint=ap-northeast-1

# 1回目
$ aws s3api put-object \--bucket${BUCKET}\--key hello.txt \--body hello.txt \--if-none-match'*'{"ETag": "\"b1946ac92492d2347c6235b4d2611184\"",
    "ServerSideEncryption": "AES256"}# 2回目
$ aws s3api put-object \--bucket${BUCKET}\--key hello.txt \--body hello.txt \--if-none-match'*'
An error occurred (PreconditionFailed) when calling the PutObject operation: At least one of the pre-conditions you specified did not hold

LocalStack で試す

実は2024年8月29日にリリースされた LocalStack v3.7.0で Amazon S3 の conditional writes(条件付き書き込み)がサポートされている❗️あまりに早すぎる \( 'ω')/

github.com

さっそく最新の LocalStack v3.7.2を使って試す.

$ localstack --version3.7.2

結論としてはまったく同じだった👌awsコマンドを awslocalコマンドに変更すれば良く,LocalStack で Amazon S3 の conditional writes(条件付き書き込み)を試せた❗️

$ echo'hello'> hello.txt

$ BUCKET=kakakakakku-sandbox-conditional-writes
$ awslocal s3api create-bucket \--bucket${BUCKET}\--create-bucket-configurationLocationConstraint=ap-northeast-1

# 1回目
$ awslocal s3api put-object \--bucket${BUCKET}\--key hello.txt \--body hello.txt \--if-none-match'*'{"ETag": "\"b1946ac92492d2347c6235b4d2611184\"",
    "ServerSideEncryption": "AES256"}# 2回目
$ awslocal s3api put-object \--bucket${BUCKET}\--key hello.txt \--body hello.txt \--if-none-match'*'
An error occurred (PreconditionFailed) when calling the PutObject operation: At least one of the pre-conditions you specified did not hold

関連記事

kakakakakku.hatenablog.com

AWS CloudFormation の Git sync を既存スタックに導入する

$
0
0

GitHub リポジトリなどにプッシュをしたら自動的にトリガーされて AWS CloudFormation スタックをデプロイできる「Git sync 機能」で,そういえば「既存スタックって途中から Git sync できる?」と疑問に感じて試してみた❗️結論から言うと既存スタックを Git sync できる👌

docs.aws.amazon.com

GitHub で試したログを簡単にまとめておく📝 ちなみに AWS CodeConnections 設定・IAM Role などは準備ができている前提とする.

AWS CloudFormation スタックを準備する

今回はシンプルに Amazon SQS キューを1つ追加する template.ymlを実装する.

Resources:Queue:Type: AWS::SQS::Queue
    Properties:QueueName: sandbox-cfn-stack-git-sync
      ReceiveMessageWaitTimeSeconds:20

AWS CLI でデプロイする.

$ aws cloudformation deploy \--stack-name sandbox-cfn-stack-git-sync \--template-file template.yml

Git sync を設定する

次にマネジメントコンソールから Git sync を設定する.Git sync に必要なデプロイファイル deployment.ymlは必要最低限にしてある👌(デプロイファイルを用意しているのに自動的にデプロイファイル追加のプルリクエストが作られる挙動はちょっと微妙だと思う...)

template-file-path: template.yml
parameters:{}tags:{}

AWS CloudFormation テンプレートを更新する

今回は Amazon SQS キューの可視性タイムアウト (VisibilityTimeout) をデフォルト値の 30秒から 60秒に更新する💡

Resources:Queue:Type: AWS::SQS::Queue
    Properties:QueueName: sandbox-cfn-stack-git-sync
      ReceiveMessageWaitTimeSeconds:20VisibilityTimeout:60

GitHub リポジトリにプッシュをすると期待通りにデプロイされていた👌

Before

$ aws sqs get-queue-attributes \--queue-url https://sqs.ap-northeast-1.amazonaws.com/000000000000/sandbox-cfn-stack-git-sync \--attribute-names All | grep VisibilityTimeout
        "VisibilityTimeout": "30",

After

$ aws sqs get-queue-attributes \--queue-url https://sqs.ap-northeast-1.amazonaws.com/000000000000/sandbox-cfn-stack-git-sync \--attribute-names All | grep VisibilityTimeout
        "VisibilityTimeout": "60",

ちなみに

2024年8月16日に What's New に出たのにその後 404 になってしまった「AWS CloudFormation Git sync now supports pull request workflows to review your stack changes」というリリースはどうなったんだろう💨 AWS CloudFormation の Git sync 機能でプルリクエストを出したら自動的に変更セットを作ってくれると開発フロー的にはもっと使いやすくなるんだけどなぁ〜❗️期待値して待とう \( 'ω')/


Vercel Terraform Provider: Vercel の設定を Terraform で管理しよう

$
0
0

最近 Vercelを使っていて,Vercel コンソールでプロジェクト設定をしてしまったけど,継続的なプロダクション運用を考えたらコンソール操作は避けたいな〜と思って,Vercel Terraform Providerを試してみた❗️結論から言うと,期待した通りに Vercel の設定を宣言的に管理できて良かった👌 既に Terraform を使っているなら特にハマることなく導入できそう.

github.com

以下のドキュメントを読むと Next.js アプリケーションを Vercel Terraform Provider で Vercel にデプロイする流れを試せる📝 今回はこのドキュメントを参考にしつつ,個人的に気になる設定をデプロイしてみた \( 'ω')/

vercel.com

サンプルアプリ

Vercel にデプロイするアプリは何でも良くて,今回は2024年5月に React-Adminに入門するときに使った React-Admin Tutorial アプリ(GitHub プライベートリポジトリにある)を使うことにした.

kakakakakku.hatenablog.com

1. Vercel GitHub App に権限を付与する

まずは Vercel GitHub Appで連携する GitHub リポジトリに対する権限を付与しておく👌

2. Vercel Token を発行する

次に Vercel Token を発行して環境変数 VERCEL_API_TOKENに設定しておく🔑

$ exportVERCEL_API_TOKEN=xxx

vercel.com

3. terraform init コマンドを実行する

providers.tfを実装して Vercel Terraform Provider を取得する.今回は最新 v1.13.0を使う.

👾 providers.tf

terraform{required_providers{vercel = {source  = "vercel/vercel"version = "~> 1.13"}}}

4. Project: terraform plan コマンドを実行する

次に projects.tfを実装する.まずは Vercel Project を vercel_projectリソースを使って追加する.React-Admin は Viteを使ってビルドをしているため frameworkviteにした👌

👾 projects.tf

resource"vercel_project""react_admin_tutorial"{name      = "react-admin-tutorial"framework = "vite"git_repository = {type = "github"repo = "kakakakakku/react-admin-tutorial"}
}

そして terraform planコマンドを実行する.問題なさそう❗️ちなみに次に設定を変更する予定の vercel_authentication.deployment_typeはデフォルトでは standard_protectionになっていた💡

$ terraform plan
Terraform will perform the following actions:

  # vercel_project.react_admin_tutorial will be created
  + resource "vercel_project""react_admin_tutorial"{
      + auto_assign_custom_domains                        =true
      + automatically_expose_system_environment_variables =(known after apply)
      + customer_success_code_visibility                  =(known after apply)
      + directory_listing                                 =(known after apply)
      + framework                                         ="vite"
      + function_failover                                 =(known after apply)
      + git_fork_protection                               =true
      + git_lfs                                           =(known after apply)
      + git_repository                                    ={
          + production_branch =(known after apply)
          + repo              ="kakakakakku/react-admin-tutorial"
          + type="github"}
      + id                                                =(known after apply)
      + name                                              ="react-admin-tutorial"
      + oidc_token_config                                 ={
          + enabled =false}
      + prioritise_production_builds                      =(known after apply)
      + protection_bypass_for_automation_secret           =(known after apply)
      + serverless_function_region                        =(known after apply)
      + team_id                                           =(known after apply)
      + vercel_authentication                             ={
          + deployment_type ="standard_protection"}}

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

5. Project: terraform apply コマンドを実行する

最後は terraform applyコマンドを実行する.

$ terraform apply

vercel_project.react_admin_tutorial: Creating...
vercel_project.react_admin_tutorial: Creation complete after 2s [id=prj_xxxxx]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

できた❗️最高 \( 'ω')/

Vercel Project を追加できた

6. Deployments: terraform plan コマンドと terraform apply コマンドを実行する

そして Vercel Project をデプロイするために vercel_deploymentリソースを使う.今回はシンプルに GitHub の mainブランチをデプロイする設定にした👌

👾 deployments.tf

resource"vercel_deployment""react_admin_tutorial"{project_id = vercel_project.react_admin_tutorial.id
  ref        = "main"}

同じく terraform planコマンドと terraform applyコマンドを実行する.

$ terraform plan
Terraform will perform the following actions:

  # vercel_deployment.react_admin_tutorial will be created
  + resource "vercel_deployment""react_admin_tutorial"{
      + domains    =(known after apply)
      + id         =(known after apply)
      + production =(known after apply)
      + project_id ="prj_xxxxx"
      + ref        ="main"
      + team_id    =(known after apply)
      + url        =(known after apply)}

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

$ terraform apply
vercel_deployment.react_admin_tutorial: Creating...
vercel_deployment.react_admin_tutorial: Still creating... [10s elapsed]
vercel_deployment.react_admin_tutorial: Still creating... [20s elapsed]
vercel_deployment.react_admin_tutorial: Still creating... [30s elapsed]
vercel_deployment.react_admin_tutorial: Still creating... [40s elapsed]
vercel_deployment.react_admin_tutorial: Creation complete after 49s [id=dpl_xxx]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

少し待っているとデプロイされた \( 'ω')/

Vercel に React-Admin Tutorial アプリをデプロイできた 1

Vercel に React-Admin Tutorial アプリをデプロイできた 2

7. Project: terraform plan コマンドと terraform apply コマンドを実行する

Vercel Project では Deployment Protection のデフォルト設定として Vercel Authenticationが有効化されている🛡

vercel.com

よって,Vercel へのアクセス権限がないとサイトにアクセスできず,例えば Google Chrome のシークレットウィンドウでアクセスしようとすると拒否される🛑

Vercel Authentication でアクセスできなかった

そこで projects.tfを修正して vercel_authentication.deployment_typeをデフォルトの standard_protectionから noneに修正する💡

👾 projects.tf

resource"vercel_project""react_admin_tutorial"{name      = "react-admin-tutorial"framework = "vite"vercel_authentication = {deployment_type = "none"}git_repository = {type = "github"repo = "kakakakakku/react-admin-tutorial"}
}

terraform planコマンドと terraform applyコマンドを実行する.

$ terraform plan
Terraform will perform the following actions:

  # vercel_project.react_admin_tutorial will be updated in-place
  ~ resource "vercel_project""react_admin_tutorial"{
        id                                                ="prj_xxxxx"
        name                                              ="react-admin-tutorial"
      + protection_bypass_for_automation_secret           =(known after apply)
      + team_id                                           =(known after apply)
      ~ vercel_authentication                             ={
          ~ deployment_type ="standard_protection" ->"none"}# (12 unchanged attributes hidden)}

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

$ terraform apply
vercel_project.react_admin_tutorial: Modifying... [id=prj_xxxxx]
vercel_project.react_admin_tutorial: Modifications complete after 1s [id=prj_xxxxx]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

Vercel Authentication を無効化できて Google Chrome のシークレットウィンドウでもアクセスできるようになった👌

Vercel Authentication を無効化できた

8. Domains: terraform plan コマンドと terraform apply コマンドを実行する

最後は Vercel Project のデフォルトドメイン *.vercel.appにカスタムドメインを割り当てる❗️カスタムドメインは vercel_project_domainリソースを使って割り当てられる.なお前提として,以下のドキュメントを参考に Amazon Route 53 で取得したドメインのホストゾーンに cname.vercel-dns.com.という CNAME レコードを登録しておく👌

vercel.com

👾 domains.tf

resource"vercel_project_domain""react_admin_tutorial"{project_id = vercel_project.react_admin_tutorial.id
  domain     = "react-admin-tutorial.xxxxx.com"}

同じく terraform planコマンドと terraform applyコマンドを実行する.

$ terraform plan
Terraform will perform the following actions:

  # vercel_project_domain.react_admin_tutorial will be created
  + resource "vercel_project_domain""react_admin_tutorial"{
      + domain     ="react-admin-tutorial.xxxxx.com"
      + id         =(known after apply)
      + project_id ="prj_xxx"
      + team_id    =(known after apply)}

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

$ terraform apply
vercel_project_domain.react_admin_tutorial: Creating...
vercel_project_domain.react_admin_tutorial: Creation complete after 1s [id=react-admin-tutorial.xxxxx.com]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

カスタムドメインを設定できた❗️そして https://react-admin-tutorial.xxxxx.comでアクセスできるようになった \( 'ω')/

カスタムドメインを設定できた

まとめ

Vercel Terraform Providerを使って Vercel の設定を Terraform で宣言的に管理しよう❗️

関連記事

kakakakakku.hatenablog.com

LocalStack で Lambda の Errors メトリクスを SNS 経由でアラートする

$
0
0

LocalStack を使って AWS Lambda 関数の Errorsメトリクスをモニタリングして Amazon CloudWatch Alarm と Amazon SNS でアラートするアーキテクチャを試してみた❗️LocalStack(無料版)ですべて試せる〜 \( 'ω')/

アーキテクチャ図

Webhook.site

今回は Amazon SNS からアラートを受け取るお手軽な環境として Webhook.siteを使う.検証用途などに使える一時的な Webhook URL を発行できて,Amazon SNS の HTTPS エンドポイントとしてサブスクライブできる👌ウェブサイトにアクセスして Your unique URLに表示されている URL をコピーしておく.

webhook.site

デプロイ

まずは AWS SAM (AWS CloudFormation) で LocalStack 上に検証環境をデプロイする.

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Parameters:WEBHOOK:Type: String
    AllowedPattern: https://webhook.site/.*

Resources:Function:Type: AWS::Serverless::Function
    Properties:FunctionName: sandbox
      CodeUri: ./src
      Handler: app.lambda_handler
      Runtime: python3.12
      Architectures:- x86_64
  Alarm:Type: AWS::CloudWatch::Alarm
    Properties:AlarmName: function-errors
      MetricName: Errors
      Namespace: AWS/Lambda
      ComparisonOperator: GreaterThanOrEqualToThreshold
      EvaluationPeriods:1Period:60Statistic: Sum
      Threshold:1AlarmActions:- !Ref Topic
      Dimensions:- Name: FunctionName
          Value:!Ref Function
  Topic:Type: AWS::SNS::Topic
    Properties:DisplayName: alerts
      Subscription:- Protocol: https
          Endpoint:!Ref WEBHOOK

そして samlocalコマンドを使ってデプロイする.なお,デプロイ時の WEBHOOKパラメータには取得した Webhook.site の URL を設定しておく.

$ samlocal build
$ samlocal deploy --parameter-overridesWEBHOOK=https://webhook.site/64e8d8c6-3fa2-4d68-a2a2-a87409553fff

すると Webhook.site に SubscriptionConfirmationの通知が届くので,SubscribeURLにアクセスしてサブスクライブを承認しておく❗️これで準備 OK〜

SubscriptionConfirmation の通知が届いた

AWS Lambda 関数を実行する

今回はサンプルとして必ずエラーになる AWS Lambda 関数 (Python) をデプロイした💡

deflambda_handler(event, context):
    raiseException('Always an error!')

さっそく awslocalコマンドを使って AWS Lambda 関数を実行する.

$ awslocal lambda invoke --function-name sandbox outputs.json
{"StatusCode": 200,
    "FunctionError": "Unhandled",
    "ExecutedVersion": "$LATEST"}

すると Webhook.site にアラートが届いた👌

Webhook.site にアラートが届いた

LocalStack Resource Browser

LocalStack Resource Browser で Amazon CloudWatch の Alarms を開くと,期待通りに AWS Lambda 関数 sandboxの Errors メトリクスが 1になっていた❗️

Errors メトリクス

お掃除

念のため Webhook.site の Moreメニューで Delete URLを実行しておくと安心かなと思う👌

CloudFormation Git sync で変更セットの結果をプルリクエストにコメントする

$
0
0

2024年9月20日にリリースされたアップデートによって,AWS CloudFormation の「Git sync 機能」で GitHub リポジトリにプルリクエストを出したら自動的に変更セットの結果をコメントしてくれるようになった❗️

aws.amazon.com

試す

Git sync の設定画面で Enable comment on pull requestを有効化にしておけば OK👌

今回は Amazon SQS キューの可視性タイムアウト (VisibilityTimeout) を 60秒から 90秒に更新する💡

Resources:Queue:Type: AWS::SQS::Queue
    Properties:QueueName: sandbox-cfn-stack-git-sync
      ReceiveMessageWaitTimeSeconds:20VisibilityTimeout:90

GitHub リポジトリにプッシュをして mainブランチに対してプルリクエストを出したら,すぐにコメントが返ってきた❗️

そして Click here to view change detailsの表を右にスクロールすると

  • BeforeValue: 60
  • AfterValue: 90
  • AttributeChangeType: Modify

と書いてあって,変更セットの結果を確認できる❗️

確認してからプルリクエストをマージすると,期待通りにデプロイされてた〜

$ aws sqs get-queue-attributes \--queue-url https://sqs.ap-northeast-1.amazonaws.com/000000000000/sandbox-cfn-stack-git-sync \--attribute-names All | grep VisibilityTimeout
        "VisibilityTimeout": "90",

参考記事

AWS CloudFormation の Git sync 機能は既存の AWS CloudFormation スタックにも導入できることを前に確認したので合わせて読んでもらえればと〜📝

kakakakakku.hatenablog.com

Powertools for AWS Lambda (Python) で Cognito User Pools の トークン生成前トリガーを実装しよう

$
0
0

Amazon Cognito User Pools には AWS Lambda 関数を使って認証フローをカスタマイズできる「Lambda トリガー」という機能がある💡トークン生成前トリガー (Pre token generation Lambda trigger) を使うと,認証時に発行されるトークン (ID Token / Access Token) をカスタマイズできる👌

docs.aws.amazon.com

docs.aws.amazon.com

ちなみにトークン (Access Token) は2023年12月のリリースでカスタマイズできるようになっていたりする💡

aws.amazon.com

Powertools for AWS Lambda (Python)

Powertools for AWS Lambda (Python) の Event Source Data Classesを使うと,シンプルに Lambda トリガーを実装できるようになる.今回は PreTokenGenerationTriggerEventを使ってトークン (ID Token) のペイロードにクレームを追加/削除するトークン生成前トリガーを実装する❗️

docs.powertools.aws.dev

準備

まずは Amazon Cognito User Pools を作っておく❗️今回はサクッと認証を試すためにアプリケーションクライアントの認証フローとして ALLOW_USER_PASSWORD_AUTHも有効化する.あとカスタム属性として X アカウント名を表す custom:xを追加しておく.その他はデフォルト設定で良いかなと思う👌

トークンを取得する(トークン生成前トリガーなし)

まずは「トークン生成前トリガーなし」でトークン (ID Token) を取得する.AWS CLI で aws cognito-idp initiate-authコマンドを実行する💡

awscli.amazonaws.com

$ CLIENT_ID=xxx
$ EMAIL=xxx
$ PASSWORD=xxx

$ aws cognito-idp initiate-auth \--client-id${CLIENT_ID}\--auth-flow USER_PASSWORD_AUTH \--auth-parametersUSERNAME=${EMAIL},PASSWORD=${PASSWORD} | jq .
{"ChallengeParameters": {},
  "AuthenticationResult": {"AccessToken": "xxx",
    "ExpiresIn": 3600,
    "TokenType": "Bearer",
    "RefreshToken": "xxx",
    "IdToken": "xxx"}}

取得した IdTokenの値から JWT Payload を抜き出すと以下のようになっていた❗️追加したカスタム属性 custom:xにも @kakakakakkuという値が入っている👌

{"sub": "f7a4da98-a041-70e4-7954-8bbc5d3ca764",
  "email_verified": true,
  "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_xxxxxxxxx",
  "cognito:username": "f7a4da98-a041-70e4-7954-8bbc5d3ca764",
  "custom:x": "@kakakakakku",
  "origin_jti": "85ed5e84-1346-41fa-8269-1e5aa0a35bab",
  "aud": "xxx",
  "event_id": "525e5170-54f5-450c-a053-258fde4ea50f",
  "token_use": "id",
  "auth_time": 1726876453,
  "exp": 1726880053,
  "iat": 1726876453,
  "jti": "db5441ca-8710-4fe7-b0cc-4b31f6fc77e0",
  "email": "y.yoshida22@gmail.com"
}

トークン (ID Token) の詳細は以下のドキュメントに載っている📝

docs.aws.amazon.com

トークン生成前トリガー

次に AWS Lambda 関数を実装してトークン生成前トリガーを設定する.

AWS Lambda 関数の実装は Powertools for AWS Lambda (Python) の Event Source Data Classesを使って,my_keyクレームの追加と custom:xクレームの削除をする.実装自体は簡単にできる👌

👾 app.py(サンプル1)

シンプルに実装する場合

from aws_lambda_powertools.utilities.data_classes.cognito_user_pool_event import PreTokenGenerationTriggerEvent


deflambda_handler(event, context):
    event: PreTokenGenerationTriggerEvent = PreTokenGenerationTriggerEvent(event)
    event.response.claims_override_details.claims_to_add_or_override = {'my_key': 'my_value'}
    event.response.claims_override_details.claims_to_suppress = ['custom:x']

    return event.raw_event

👾 app.py(サンプル2)

ハンドラとロジックを分離してテストをしやすく実装する場合

import json

from aws_lambda_powertools.utilities.data_classes.cognito_user_pool_event import PreTokenGenerationTriggerEvent


defmain(event):
    event: PreTokenGenerationTriggerEvent = PreTokenGenerationTriggerEvent(event)
    event.response.claims_override_details.claims_to_add_or_override = {'my_key': 'my_value'}
    event.response.claims_override_details.claims_to_suppress = ['custom:x']

    return event.raw_event


deflambda_handler(event, context):
    return main(event)


if __name__ == '__main__':
    withopen('./events/event.json', 'r') as f:
        event = json.load(f)
    print(main(event))

トークンを取得する(トークン生成前トリガーあり)

もう一度 AWS CLI で aws cognito-idp initiate-authコマンドを実行して,IdTokenの値から JWT Payload を抜き出すと以下のようになっていた❗️期待通りに my_keyが追加されていて,カスタム属性 custom:xは削除されていた👌

{"sub": "f7a4da98-a041-70e4-7954-8bbc5d3ca764",
  "email_verified": true,
  "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_xxxxxxxxx",
  "cognito:username": "f7a4da98-a041-70e4-7954-8bbc5d3ca764",
  "origin_jti": "888ee4b8-4c3b-4a78-a34c-7e5f04c3788c",
  "aud": "xxx",
  "event_id": "bf25e9fe-beb5-4ad2-b1c7-188eda16ea75",
  "token_use": "id",
  "auth_time": 1726876789,
  "my_key": "my_value",
  "exp": 1726880389,
  "iat": 1726876789,
  "jti": "f75d7eaa-c826-4c40-9951-6dbe8bcc100a",
  "email": "y.yoshida22@gmail.com"
}

まとめ

Powertools for AWS Lambda (Python) の Event Source Data Classesを使って Amazon Cognito User Pools のトークン生成前トリガーをシンプルに実装するサンプルの紹介でした〜 \( 'ω')/

Lambda@Edge と CloudFront Functions に入門できるワークショップ「Handling Rewrites and Redirects using Edge Functions」

$
0
0

Lambda@Edge と CloudFront Functions を使って HTTP リクエストのリダイレクトとリライトを体験するワークショップ「Handling Rewrites and Redirects using Edge Functions」を実施してみた❗️

Lambda@Edge と CloudFront Functions に入門したいな〜という人に特におすすめ💡アーキテクチャ図も多く載っていてイメージしやすく構成されているのも良かった〜 \( 'ω')/

catalog.us-east-1.prod.workshops.aws

目次

大きく2部構成になっていて,Module 1 では Lambda@Edgeを使ってキャッシュ可能なリダイレクトとリライトを体験して,Module 2 では CloudFront Functionsを使って動的なリダイレクトとリライトを体験する📝

  • Getting Started
  • Overview
  • Module 1 - Cacheable Use Cases
    • URI based Redirects
    • Geo Location Redirects
    • Device Redirects
    • URI based Rewrites
  • Module 2 - Dynamic Use Cases
    • Dynamic Geo Location Redirects
    • Cookie Based Redirect
    • Bot Signatures Based Rewrite
  • Conclusion

所要時間はワークショップの冒頭に以下のように書いてあって妥当だと思う👌僕は計3ポモドーロ (90 minutes) で最後のクリーンアップまで完了できた.

This workshop takes about 90-120 minutes to complete all the labs covered.

Lambda@Edge と CloudFront Functions の比較

Lambda@Edge と CloudFront Functions の比較はワークショップ (Overview) で出てくるけど,ワークショップ環境を AWS CloudFormation で構築する待ち時間などに以下のドキュメントを軽く読んでおくと理解しやすくなって良いと思う💪

docs.aws.amazon.com

Module 1

Module 1 では Lambda@Edge を使って4種類のリダイレクトとリライトを体験する.Lambda@Edge のコード (Python 3.9) はコピペする.シンプルな実装で理解しやすいし,AWS Lambda 関数の実装に慣れているから特に詰まるところもなかった💡

Lambda@Edge に Amazon CloudFront のトリガーを設定して,AWS CloudShell から curlコマンドを実行しながらリダイレクト・リライトの挙動確認や X-Cacheヘッダーの結果を確認していく.

また2番目の Geo Location Redirectsでは Amazon CloudFront で CloudFront-Viewer-Countryヘッダーを追加したりする.勉強になる👌

docs.aws.amazon.com

そして4番目に試す URI based Rewritesもプロダクション運用でニーズがありそうで実践的だな〜と思った💡

Module 1 の実施ログ

最終的に4種類の Lambda@Edge を追加した.

Module 1 で追加した Lambda@Edge

Module 2

Module 2 では CloudFront Functions を使って3種類のリダイレクトとリライトを体験する.CloudFront Functions の言語は JavaScript なので TypeScript で実装してデプロイできたら良さそうだな〜なんて考えながら進めていた👀

3番目に試す Bot Signatures Based Rewriteは AWS WAF と組み合わせてボット対応を体験する.awswaf:managed:aws:bot-control:bot:category:social_mediaラベルを使ったりして,実践的で良かった🛡️

Module 2 で設定した AWS WAF Web ACLs

最終的に3種類の CloudFront Functions を追加した.

Module 2 で追加した CloudFront Functions

まとめ

Lambda@Edge と CloudFront Functions に入門できるワークショップ「Handling Rewrites and Redirects using Edge Functions」を実施してみた❗️面白かった〜 \( 'ω')/

ちなみに朝活でワークショップを実施した🌅朝活だと頭もスッキリしてて集中して取り組めるからおすすめ.

ワークショップは AWS Workshopsで探せるよ〜

awsworkshop.io

Viewing all 902 articles
Browse latest View live