こんにちは、SCSK小澤です。 生成AIを利用して自然言語によるデータ検索や要約を可能にしてくれる、Snowflake Intelligenceが一般提供(GA)になりました。 前回のブログでは、Snowflake Intelligenceの概要紹介や、伴走支援のご案内していました。 → SCSKはSnowflake Intelligenceのローンチパートナーとなりました! Snowflake Intelligenceで始める生成AI活用 ― SCSKが伴走します Snowflakeが最近GAしたSnowflake Intelligenceについての記事です。利用する為の過程・SCSKならではの支援について案内します! blog.usize-tech.com 2025.12.08 SCSKでは、社内のデータ活用基盤としてもSnowflakeを採用しており、Snowflake Intelligenceの利用についても取り組んでいます。 今回はSnowflake Intelligenceの活用PoCを始める流れについてご紹介いたします! SCSKの社内データ活用 データ活用のアーキテクチャ SCSKでは、以下のようなメダリオンアーキテクチャをSnowflake上に構築してデータを管理しています。 ブロンズ層: ファイルやシステムのデータベースなど、データソースのデータをそのまま保持する シルバー層: 分析しやすいように整形した、汎用的に利用可能なテーブルがある層 ゴールド層: BIで可視化するために、ダッシュボードごとに作成するテーブルがある層 ゴールド層に作成したテーブルをBIツール(Power BI)でインポートして、ダッシュボードを作成しています。 定型ダッシュボード作成によるデータ活用の課題 定型ダッシュボード作成に基づくデータ活用には以下のような課題があります。 ダッシュボード公開までのリードタイムが長い ニーズの把握からダッシュボードの公開まで、レイアウト検討・データモデリング・ストアドプロシージャ作成・BIツールでの実装など、ダッシュボード開発には必要な工程が多くあります。簡単な集計値の可視化であっても、実際に見たいデータが見れるようになるまでには、リードタイムが発生します。 限定的な分析軸での分析しかできない ダッシュボードでは、組織別や年度別など、あらかじめ決められた分析軸での集計値しか確認することができません。たとえば、売上が伸びた要因を分析したくても、ダッシュボードで表示されている以上の情報を得ることはできず、発生事象の理由を分析することが難しいです。 そもそもダッシュボードで情報を探すことが面倒 私たちはプライベートでも業務でも、なにかを知りたいときにはまず生成AIに問い合わせるようなりました。チャットベースでの検索に慣れすぎてしまいました。ダッシュボードを量産しても、ユーザは自分が欲しい情報があるダッシュボードがすぐに見つけられないとストレスを感じ、データ活用のモチベーションが下がってしまいます。 このように、定形ダッシュボード作成によるデータの可視化だけでは課題があります。 「必要な情報にすぐにアクセスできること」「多角的な分析が容易にできること」が、今後のデータ活用に求められています。 Snowflake Intelligenceの導入 Snowflake Intelligenceは、そんな課題を解決してくれるサービスです。 SCSKの社内データ活用においてもダッシュボードの作成と並行して、Snowflake Intelligenceの活用検討を行いPoCを開始しました。 Snowflake Intelligenceは、以下のような全体像になります。 一番下のDataから辿って、Snowflake Intelligenceに表示するまでの流れをご紹介します! Data:分析対象とするデータの選定 メダリオンアーキテクチャにおけるシルバー層は、汎用的に利用できるテーブルを保持する層でした。 ダッシュボード開発では、このシルバー層のテーブルをもとにダッシュボード用のテーブルをゴールド層に作成しますが、当プロジェクトではSnowflake Intelligenceからこのシルバー層のテーブルにそのままアクセスできるようしました。 これにより、ユーザーが自由に問い合わせても幅広い分析軸での返答を得ることができます。 ディメンショナルモデリングに基づき、すでに約100以上のファクト/ディメンションのテーブルを用意していましたが、PoCとしてはそのうちの、PLデータ・引合データ・予算データなど主要なテーブルを対象に絞ることとしました。 データ準備:Semantic Viewの作成 先ほど選定したテーブルを対象に、Semantic Viewを作成します。 Semantic Viewの作成はファクトテーブルだけでなく、関連するマスタテーブルの作成も必要になります。 そのため、今回PoCとして選定したファクトテーブルは5テーブル程でしたが、それぞれのファクトテーブルに関連するマスタテーブルも含めると、約15テーブルのSemantic Viewの作成が必要になります。 それぞれのテーブルのカラムには数十のカラムがあり、Semantic Viewの作成を愚直に行っていると、PoCがなかなか始められません。 そこで、Snowflake 菅野様が公開されている Snowflake のセマンティックビューを AI で自動生成しよう のストアドプロシージャで、Semantic Viewを自動作成することとしました。 将来的には、公式機能としてSemantic Viewの自動作成機能もリリースされるようです。 2026/1現在は未リリースのようですが、最新のリリース状況をご確認ください。 各テーブルのSemantic Viewを作成した後、それぞれのSemantic View間(ファクトテーブル – マスタテーブル間)のリレーションの設定が必要になります。 リレーションの設定には、同じSemantic Viewの定義内にリレーションの設定先のマスタテーブルの定義も含めておく必要があります。複数のファクトテーブル(PL、引合)から同一のマスタテーブル(組織マスタ、企業マスタ)を参照する場合もありますが、それぞれのファクトテーブルの定義ごとに、マスタテーブルの定義を含めておく必要があります。 そのため、以下のように作成されたマスタテーブルのYAML定義をコピーして、ファクトテーブルのYAML定義内にマージさせていく必要があります。その後、マスタテーブルへのリレーション設定を行います。 オーケストレーション:Cortex Agentsの作成 作成したSemantic Viewを元に、Cortex Agentを作成します。 Snowflake Intelligenceの回答精度を上げるためには、関連のないデータのSemantic Viewを1つのCortex Agentに詰め込むべきではありません。 そのため、今回はPLエージェント、引合エージェントなど、Semantic ViewのそれぞれでCortex Agentを作成しています。 ユーザには、Snowflake Intelligenceの画面上から問い合わせたい内容に合ったエージェントを選択してもらいます。 ただし、それぞれのCortex Agentに対して1つのSemantic Viewsを作成していると、汎用的に回答できるはずのエージェントとしての有用性が薄れてしまう部分もあるので、どのようにCortex Agentにまとめていくかは考察の余地があります。 Cortex Agentsの設定では、「オーケストレーション手順」の設定も重要になります。 たとえば、”年度単位で売上を集計して下さい”と問い合わせた場合、1~12月で集計されてしまうため、4~翌3月で集計してもらいたい場合には以下のような「オーケストレーション手順」の設定を行います。 “年度”単位での集計・可視化の依頼が来た場合には、4月から3月の12カ月を集計期間として、分析をお願いいたします。例えば、2024年度の売上分析をしてください。という質問に対しては2024年4月から2025年3月までの集計期間で売上を分析するようにしてください。 Snowflake Intelligenceの利用デモ Snowflake Intelligenceを実際に利用してみます。 PLに関する問い合わせを行いたいため、エージェントは「PL」を指定しておきます。 まずは、利益率の経年推移をみてみます。 すると、2023/3に利益率が大きく下がっていることが確認できました。 ここまでは定型ダッシュボードでも確認できますが、ここから”なぜそうなっているのか?”を深堀して分析していくようなことは、Snowflake Intelligenceでないとできません。 次に、2023/3に利益がワースト3の案件を取得してみます。 特定の案件が抽出できました。 このワースト1位の案件について、月別の利益の傾向を見てみます。 Snowflake Intelligenceで、グラフでの可視化に加え、トレンドの分析やなぜそのようになったかの推察まで行ってくれています。 このSnowflake Intelligenceの分析結果を踏まえて、 AIによる考察を案件担当者に事実確認 案件悪化パターンを分析 悪化の変調をとらえるKPI設定し、ダッシュボードに追加 悪化案件が多い組織の特徴抽出 など、実際にビジネスへのアクションにつなげていくことが考えられます! 精度向上のためのポイント Snowflake Intelligenceを実際に業務で利用していくには、回答速度や精度を向上させていく必要があります。 回答速度 現状は回答までに30秒以上かかっています。モニタリングから内訳を確認してみると、「SQL実行」は5秒程度ですが、それ以外の「LLMの計画」「LLM応答生成」の時間が多く占めています。 このような場合、ウェアハウスの性能を上げても大きな改善は見られず、LLMの回答速度を向上させていく必要があります。LLMの回答速度を向上させるためには、想定される質問をあらかじめ「検証済みクエリ」として登録しておくことが重要です。 回答精度 AI機能を使って自動生成したSemantic Viewをそのまま使っているため、まだまだ回答された数値の精度が出ていません。 回答の精度を向上させるには、実行されたクエリを確認して正しいものは「検証済みクエリ」として登録したり、間違っている場合は同義語を見直すなど、地道な調整が必要になります。 Semantic View精度向上のために、主に以下の設定を見直す必要があります。 まとめ Snowflake Intelligenceを実際に利用するまでの手順についてご紹介しました。 社内のデータ活用における現状としてはまだまだ回答の精度が得られず、Semantic Viewの設定見直しなどを行っています。 回答精度を向上させるための具体的な手順についても検証ができたら、また共有させていただきたいと思います!
AWSを利用するうえで一時的に別のIAMロールの権限に切り替えて作業するためにスイッチロールをすることがあるかと思います。そんな中で最近 AWS CLI を利用してのスイッチロールをする機会があったのですが、やり方がわからず手間取ったので備忘として紹介できればと思います。ここでは2つ方法を紹介しますので、ぜひ参考にしてみてください。 方法1 -configファイルの修正- 一つ目の方法はconfigファイルを修正する方法です。 以下パスにあるファイルをメモ帳等で開きます。 "C:\Users\<ユーザ名>\.aws\config" 以下を追記し保存します。 [profile <任意のプロファイル名>] #スイッチロール先のARN role_arn = arn:aws:iam::<スイッチロール先のアカウントID>:role/<ロール名> source_profile = default #リージョン ex:ap-northeast-1 region = <リージョン名> #MFA識別子。MFA認証がある場合 mfa_serial = arn:aws:iam::<アカウントID>:mfa/<ユーザ名> あとはコマンド実行時に「––profile <任意のプロファイル名>」をつけるだけでスイッチ先のロールで実行できるようになります。 aws sts get-caller-identity --profile <任意のプロファイル名> この方法はconfigファイルを修正し、コマンドごとに「–profile <任意のプロファイル名>」をつけるだけなので簡単です。 方法2 -一時的なアクセスキーの取得- 2つ目の方法は一時的なアクセスキーを取得し設定する方法です。 以下コマンドでスイッチロール先の情報を取得します。 aws sts assume-role –-role-arn <スイッチロール先のarn> --role-session-name <任意のセッション名> このコマンドを実行すると一時的なアクセスキー、シークレットアクセスキー、セッショントークンが表示されるので、以下コマンドを用いてそれぞれ設定します #取得したアクセスキー入力 set AWS_ACCESS_KEY_ID= <アクセスキー> #取得したシークレットアクセスキー入力 set AWS_SECRET_ACCESS_KEY= <シークレットアクセスキー> #取得したセッショントークン入力 set AWS_SESSION_TOKEN= <セッショントークン> 以下コマンドでスイッチロールしたことを確認します。 aws sts get-caller-identity これでスイッチロール完了です。方法1よりかは手間かもしれませんが毎回「–profile ~」をつけてコマンド実行するのが面倒という人には良いかもしれません。 おわりに 今回はAWS CLIでのスイッチロールについて紹介しました。ぜひ参考にしてください。
1. はじめに こんにちは。SCSK志村です。 Azure Monitor AgentでVMのカスタムログを収集する際、以下のようにフォルダでワイルドカード指定をしたいことがあると思います。 C:\bat\log**.log 従来、Azure Monitor Agentではフォルダに対するワイルドカード指定はできない認識でしたが、公式サイトを確認したところ 「ファイルの一つ上の階層のフォルダレベルではワイルドカード指定が可能」 と記載されていました。 Azure Monitor を使用して仮想マシンからテキスト ファイルを収集する - Azure Monitor Azure Monitor エージェントを使用して仮想マシン上のテキスト ファイルからログ データを収集するようにデータ収集ルールを構成します。 learn.microsoft.com 設定 説明 ファイル パターン ローカルディスク上のログファイルの場所と名前を指定します。新しい名前で毎日ファイルが作成される場合など、異なるファイル名にはワイルドカードを使用します。複数のファイルパターンをカンマで区切って入力できます。ファイル名にはワイルドカードを使用でき、ファイル名の一つ上のレベルのフォルダー名のみワイルドカード指定が可能です。 例: – C:\Logs\MyLog.txt – C:\Logs\MyLog*.txt – C:\Logs\IIS*\*.logs – C:\App01\AppLog.txt, C:\App02\AppLog.txt – /var/mylog.log – /var/mylog*.log – /var/logs/*/* 現時点でのドキュメントの更新日は2025年4月30日となっています。 公式サイトの該当ページの前バージョンも確認しましたが、フォルダのワイルドカード指定の記述はありませんでした。 ※ファイルのワイルドカード指定は以前から可能でした 前バージョンの更新日は2024年11月14日ですので、このタイミング以降に機能が追加されたと考えられます。 (2025年12月に公開情報へ追記されたとの情報もありました。ドキュメント更新日の記載とは一致しませんが、直近の更新のようです。) <2024年11月14日バージョンの記載> 公式サイトに記載されている通り、実際に検証を行いました。 2. フォルダのワイルドカード指定の検証 データソースの指定で、「.txt」というカスタムテキストファイルと、その一つ上の階層のフォルダを「*」で指定します。 また、対応する Azure Log Analytics Workspaces にテーブル「log_test_01_CL」を作成しておきます。 サーバ上で下記ファイルを更新しました。 C:\logtest\test01\test01.txt Log Analytics を確認したところ、対象ファイルが取得できていることを確認できました。 簡単な確認ではありますが、フォルダのワイルドカード指定が可能であることを確認できました。 3. まとめ Azure Monitor Agent のカスタムテキストファイル収集にて、フォルダのワイルドカード指定が可能になりました。 複数フォルダにまたがるテキストファイルを収集する設定が簡単になり、とても便利なアップデートですね。 なお、ワイルドカードの使用はファイル名の一つ上のレベルのフォルダ名のみで可能であることにご注意ください。 この記事が、Azure におけるカスタムログファイル収集の設計のご参考になれば幸いです。
こんにちは、広野です。 コンテナがらみのリソースは CI/CD 環境がないとデプロイするまでがコマンドだらけになって嫌気がさすので、いつも最初に CI/CD 環境をつくることから始めています。Amazon Bedrock AgentCore Runtime は Amazon S3 に配置したソースコードからの簡単なデプロイもできるのですが、今後のスケールも考えてコンテナで最初から作ろうと思いました。 アーキテクチャ 以下のアーキテクチャで作っています。 以前作成したコンテナ Lambda の CI/CD 環境 と 9 割以上同じです。これを書いてて思いましたが AgentCore Runtime はコンテナ Lambda を作ったことがある人なら馴染み易いですね。 どのように動くか 開発環境 (IDE) で、以下のように必要なコード群を配置して AWS CodeCommit リポジトリに push します。 コードが CodeCommit にアップロードされると、以下のように AWS CodePipeline が動き出します。画像は正常完了後のものです。 デプロイが完了すると、マネジメントコンソール内で Amazon Bedrock AgentCore Runtime が出来上がったことが確認できます。 「テストエンドポイント」を押して動作を確認してみます。 今回は、 AWS Knowledge MCP Server に問い合わせてくれる単純なエージェントを作りましたので、AWS に関する質問を投げてみます。※後ほどエージェントのコードは貼り付けます。 回答が長かったので画像は途中で切れてしまっていますが、しっかりと説明してくれました。 エージェントのコード IDE 内で作成したエージェント用のコードです。これ自体は全然大したものではないです。動いたサンプル程度に思って下さい。 app.py (エージェントロジック) シンプルに、MCP サーバーに聞きに行くだけのエージェントです。LLM は Amazon Nova 2 Lite を使っています。 import os from strands import Agent from strands.tools.mcp import MCPClient from mcp.client.streamable_http import streamablehttp_client from bedrock_agentcore import BedrockAgentCoreApp os.environ["AWS_DEFAULT_REGION"] = "ap-northeast-1" app = BedrockAgentCoreApp() mcp = MCPClient( lambda: streamablehttp_client( "https://knowledge-mcp.global.api.aws" ) ) @app.entrypoint def invoke(payload): user_message = payload.get("prompt", "プロンプトの取得に失敗したのでその旨ユーザーに伝えてください。") try: with mcp: agent = Agent( model="global.amazon.nova-2-lite-v1:0", system_prompt="""あなたは AWS の技術仕様に精通したシニアソリューションアーキテクトです。 ユーザーの質問に対して、提供されたツールを使用して AWS 公式ドキュメントから正確な情報を取得し、 丁寧かつ専門的に回答してください。情報が見つからない場合は、推測で答えず正直に伝えてください。""", tools=mcp.list_tools_sync() ) result = agent(user_message) return {"result": result.message} except Exception as e: app.logger.error(f"エージェントエラー: {e}") return {"error": "エージェントの処理中にエラーが発生しました"} if __name__ == "__main__": app.run() requirements.txt これもシンプルに、インストールが必要な Python モジュールを並べただけです。 strands-agents bedrock-agentcore mcp Dockerfile AWS CodeBuild でビルドするときに使用します。 FROM --platform=linux/arm64 public.ecr.aws/docker/library/python:3.14-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py . EXPOSE 8080 ENTRYPOINT ["python", "app.py"] 使用するコンテナイメージは Python 3.14 のスリムなものにしました。Amazon S3 からデプロイするときは執筆時点では Python 3.13 が最新のバージョンとして選べるようでしたが、コンテナイメージだとビルド環境の制約に依存しそうです。 requirements.txt を元にモジュールをインストールし、app.py を配置します。 buildspec.yml AWS CodeBuild でビルドするときに使用します。 Amazon Bedrock AgentCore Runtime は ARM64 アーキテクチャで動作するので、コンテナイメージをビルドするコマンドに明示的に ARM64 を使用するオプションを付けます。$マークの環境変数が記載されていますが、これは CodeBuild から渡されます。 version: 0.2 phases: pre_build: commands: - echo Logging in to Amazon ECR... - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com build: commands: - echo Building the Docker image... - docker build --platform linux/arm64 -t $IMAGE_REPO_NAME:$IMAGE_TAG . - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG post_build: commands: - echo Pushing the Docker image... - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG artifacts: files: - cfn_agentcore_runtime.yml cfn_agentcore_runtime.yml デプロイフェーズで使用します。 ビルドフェーズでは、ここまでのファイルを使用してコンテナイメージを作成し、Amazon ECR リポジトリに保存するまでを担当しました。 デプロイフェーズでは、作成されたコンテナイメージから Amazon Bedrock AgentCore Runtime をデプロイします。このとき、アタッチする IAM ロールも作成します。今後別の機能を使用することを想定して、広めに権限を付けています。 Parameters で定義されているパラメータは、AWS CodePipeline から環境変数を渡されオーバーライドされます。値は何でもいいのですがパラメータの定義をしておかないと、エラーになります。 AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates a Bedrock AgentCore runtime and a relevant IAM role. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SystemName: Type: String Description: System name. use lower case only. (e.g. example) Default: example MaxLength: 10 MinLength: 1 SubName: Type: String Description: System sub name. use lower case only. (e.g. prod or dev) Default: dev MaxLength: 10 MinLength: 1 ImageTag: Type: String Default: xxxxxxxxxxxxxxxxxxxx MaxLength: 100 MinLength: 1 ImgRepoName: Type: String Default: xxxxxxxxxxxxxxxxxxxx MaxLength: 100 MinLength: 1 Resources: # ------------------------------------------------------------# # Bedrock AgentCore Runtime # ------------------------------------------------------------# AgentCoreRuntime: Type: AWS::BedrockAgentCore::Runtime Properties: AgentRuntimeName: !Sub ${SystemName}_${SubName}_agent Description: !Sub AI Agent for ${SystemName}-${SubName} AgentRuntimeArtifact: ContainerConfiguration: ContainerUri: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ImgRepoName}:${ImageTag} NetworkConfiguration: NetworkMode: PUBLIC ProtocolConfiguration: HTTP RequestHeaderConfiguration: RequestHeaderAllowlist: - Authorization RoleArn: !GetAtt AgentCoreRuntimeRole.Arn Tags: Cost: !Sub ${SystemName}-${SubName} DependsOn: - AgentCoreRuntimeRole # ------------------------------------------------------------# # Bedrock AgentCore Runtime Role (IAM) # ------------------------------------------------------------# AgentCoreRuntimeRole: Type: AWS::IAM::Role Properties: RoleName: !Sub AgentCoreRuntimeRole-${SystemName}-${SubName} Description: This role allows Bedrock AgentCore Runtime to invoke models and push logs. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - bedrock-agentcore.amazonaws.com Action: - sts:AssumeRole Condition: StringEquals: "aws:SourceAccount": !Ref AWS::AccountId ArnLike: "aws:SourceArn": !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:*" Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess Policies: - PolicyName: !Sub AgentCoreRuntimePolicy-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:DescribeLogStreams - logs:CreateLogGroup Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/bedrock-agentcore/runtimes/* - Effect: Allow Action: - logs:DescribeLogGroups Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:* - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:* - Effect: Allow Action: cloudwatch:PutMetricData Resource: "*" Condition: StringEquals: cloudwatch:namespace: bedrock-agentcore - Effect: Allow Action: - logs:CreateLogGroup - logs:PutDeliverySource - logs:PutDeliveryDestination - logs:CreateDelivery - logs:GetDeliverySource - logs:DeleteDeliverySource - logs:DeleteDeliveryDestination Resource: "*" - Sid: BedrockModelInvocation Effect: Allow Action: - bedrock:InvokeModel - bedrock:InvokeModelWithResponseStream - bedrock:ApplyGuardrail Resource: - arn:aws:bedrock:*::foundation-model/* - arn:aws:bedrock:*:*:inference-profile/* - !Sub arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:* - Effect: Allow Action: - ecr:BatchGetImage - ecr:GetDownloadUrlForLayer Resource: - !Sub arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/${ImgRepoName} - Effect: Allow Action: - ecr:GetAuthorizationToken Resource: "*" - Effect: Allow Action: - bedrock-agentcore:GetWorkloadAccessToken - bedrock-agentcore:GetWorkloadAccessTokenForJWT - bedrock-agentcore:GetWorkloadAccessTokenForUserId Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/${SystemName}_${SubName}_agent-* - Effect: Allow Action: - bedrock-agentcore:GetResourceApiKey Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default/apikeycredentialprovider/* - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/* - Effect: Allow Action: - secretsmanager:GetSecretValue Resource: - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:bedrock-agentcore-identity!default/oauth2/* - !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:bedrock-agentcore-identity!default/apikey/* - Effect: Allow Action: - bedrock-agentcore:GetResourceOauth2Token Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:token-vault/default/oauth2credentialprovider/* - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/${SystemName}_${SubName}_agent-* - Effect: Allow Action: - aws-marketplace:ViewSubscriptions - aws-marketplace:Subscribe Resource: "*" Condition: StringEquals: aws:CalledViaLast: bedrock.amazonaws.com - Effect: Allow Action: - bedrock-agentcore:StartCodeInterpreterSession - bedrock-agentcore:InvokeCodeInterpreter - bedrock-agentcore:StopCodeInterpreterSession - bedrock-agentcore:GetCodeInterpreter - bedrock-agentcore:GetCodeInterpreterSession - bedrock-agentcore:ListCodeInterpreterSessions Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:aws:code-interpreter/aws.codeinterpreter.v1 - Effect: Allow Action: - bedrock-agentcore:CreateWorkloadIdentity - bedrock-agentcore:GetWorkloadAccessTokenForUserId Resource: - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default - !Sub arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/* - Effect: Allow Action: sts:GetWebIdentityToken Resource: "*" # ------------------------------------------------------------# # Output Parameters # ------------------------------------------------------------# Outputs: # AgentCore AgentRuntimeArn: Value: !GetAtt AgentCoreRuntime.AgentRuntimeArn AgentRuntimeId: Value: !GetAtt AgentCoreRuntime.AgentRuntimeId AgentRuntimeVersion: Value: !GetAtt AgentCoreRuntime.AgentRuntimeVersion CI/CD 環境の AWS CloudFormation テンプレート 最後になりましたが、まずこれを流して CI/CD 環境を構築しました。これがないと何も始まらないです。(私は) AgentCore Runtime 用に気を付けたのは、ARM64 用のビルド環境にすることです。”aws/codebuild/amazonlinux-aarch64-standard:3.0″ というイメージを指定しています。 AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates a CI/CD environment for the AI agent. The created container image runs in Amazon Bedrock AgentCore. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SystemName: Type: String Description: System name. use lower case only. (e.g. example) Default: example MaxLength: 10 MinLength: 1 AllowedPattern: "^[a-z0-9]+$" SubName: Type: String Description: System sub name. use lower case only. (e.g. prod or dev) Default: dev MaxLength: 10 MinLength: 1 AllowedPattern: "^[a-z0-9]+$" Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "General Configuration" Parameters: - SystemName - SubName Resources: # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3BucketArtifact: Type: AWS::S3::Bucket Properties: BucketName: !Sub ${SystemName}-${SubName}-aiagent-artifact LifecycleConfiguration: Rules: - Id: AutoDelete Status: Enabled ExpirationInDays: 14 PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} S3BucketLogs: Type: AWS::S3::Bucket Properties: BucketName: !Sub ${SystemName}-${SubName}-aiagent-logs LifecycleConfiguration: Rules: - Id: AutoDelete Status: Enabled ExpirationInDays: 365 PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} # ------------------------------------------------------------# # ECR # ------------------------------------------------------------# EcrRepositoryAiAgent: Type: AWS::ECR::Repository Properties: RepositoryName: !Sub ${SystemName}-${SubName}-aiagent EncryptionConfiguration: EncryptionType: AES256 ImageScanningConfiguration: ScanOnPush: true ImageTagMutability: IMMUTABLE LifecyclePolicy: LifecyclePolicyText: | { "rules": [ { "rulePriority": 1, "description": "Keep only 5 images, expire all others", "selection": { "tagStatus": "any", "countType": "imageCountMoreThan", "countNumber": 5 }, "action": { "type": "expire" } } ] } EmptyOnDelete: true Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} # ------------------------------------------------------------# # CodeCommit Repository # ------------------------------------------------------------# CodeCommitRepoAiAgent: Type: AWS::CodeCommit::Repository Properties: RepositoryName: !Sub ${SystemName}-${SubName}-aiagent RepositoryDescription: !Sub AI Agent for ${SystemName}-${SubName} Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} # ------------------------------------------------------------# # CodePipeline # ------------------------------------------------------------# CodePipelineAiAgent: Type: AWS::CodePipeline::Pipeline Properties: Name: !Sub ${SystemName}-${SubName}-aiagent PipelineType: V2 ArtifactStore: Location: !Ref S3BucketArtifact Type: S3 RestartExecutionOnUpdate: false RoleArn: !GetAtt CodePipelineServiceRoleAiAgent.Arn Stages: - Name: Source Actions: - Name: Source RunOrder: 1 ActionTypeId: Category: Source Owner: AWS Version: 1 Provider: CodeCommit Configuration: RepositoryName: !GetAtt CodeCommitRepoAiAgent.Name BranchName: main PollForSourceChanges: false OutputArtifactFormat: CODEBUILD_CLONE_REF Namespace: SourceVariables OutputArtifacts: - Name: Source - Name: Build Actions: - Name: Build RunOrder: 1 Region: !Sub ${AWS::Region} ActionTypeId: Category: Build Owner: AWS Version: 1 Provider: CodeBuild Configuration: ProjectName: !Ref CodeBuildProjectAiAgent BatchEnabled: false EnvironmentVariables: | [ { "name": "IMAGE_TAG", "type": "PLAINTEXT", "value": "#{codepipeline.PipelineExecutionId}" } ] Namespace: BuildVariables InputArtifacts: - Name: Source OutputArtifacts: - Name: Build - Name: Deploy Actions: - ActionTypeId: Category: Deploy Owner: AWS Provider: CloudFormation Version: 1 Configuration: StackName: !Sub ${SystemName}-${SubName}-agentcore-runtime Capabilities: CAPABILITY_NAMED_IAM RoleArn: !GetAtt CodePipelineDeployCreateUpdateRoleAiAgent.Arn ActionMode: CREATE_UPDATE TemplatePath: Build::cfn_agentcore_runtime.yml ParameterOverrides: !Sub '{"SystemName":"${SystemName}","SubName":"${SubName}","ImageTag":"#{codepipeline.PipelineExecutionId}","ImgRepoName":"${EcrRepositoryAiAgent}"}' InputArtifacts: - Name: Build Name: CreateOrUpdate RoleArn: !GetAtt CodePipelineDeployCreateUpdateActionRoleAiAgent.Arn RunOrder: 1 Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} DependsOn: - CodePipelineServiceRoleAiAgent - CodeBuildProjectAiAgent - CodePipelineDeployCreateUpdateActionRoleAiAgent - EcrRepositoryAiAgent # ------------------------------------------------------------# # CodePipeline Service Role (IAM) # ------------------------------------------------------------# CodePipelineServiceRoleAiAgent: Type: AWS::IAM::Role Properties: RoleName: !Sub CpServiceRoleAiAgent-${SystemName}-${SubName} Description: This role allows CodePipeline to call each stages. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - codepipeline.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: !Sub CpServicePolicyAiAgent-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - "codecommit:CancelUploadArchive" - "codecommit:GetBranch" - "codecommit:GetCommit" - "codecommit:GetRepository" - "codecommit:GetUploadArchiveStatus" - "codecommit:UploadArchive" Resource: !GetAtt CodeCommitRepoAiAgent.Arn - Effect: Allow Action: - "codebuild:BatchGetBuilds" - "codebuild:StartBuild" - "codebuild:BatchGetBuildBatches" - "codebuild:StartBuildBatch" Resource: "*" - Effect: Allow Action: - "cloudwatch:*" - "s3:*" Resource: "*" - Effect: Allow Action: - "lambda:InvokeFunction" - "lambda:ListFunctions" Resource: "*" - Effect: Allow Action: "sts:AssumeRole" Resource: - !GetAtt CodePipelineDeployCreateUpdateActionRoleAiAgent.Arn DependsOn: - CodeCommitRepoAiAgent - CodePipelineDeployCreateUpdateActionRoleAiAgent # ------------------------------------------------------------# # CodePipeline Deploy Create Update Role (IAM) # ------------------------------------------------------------# CodePipelineDeployCreateUpdateRoleAiAgent: Type: AWS::IAM::Role Properties: RoleName: !Sub CpCrUpdRoleAiAgent-${SystemName}-${SubName} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: cloudformation.amazonaws.com Version: "2012-10-17" Path: / Policies: - PolicyName: !Sub CpCrUpdPolicyAiAgent-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Action: "*" Effect: Allow Resource: "*" # ------------------------------------------------------------# # CodePipeline Deploy Create Update Action Role (IAM) # ------------------------------------------------------------# CodePipelineDeployCreateUpdateActionRoleAiAgent: Type: AWS::IAM::Role Properties: RoleName: !Sub CpCrUpdActionRoleAiAgent-${SystemName}-${SubName} AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: AWS: Fn::Join: - "" - - "arn:" - Ref: AWS::Partition - ":iam::" - Ref: AWS::AccountId - :root Version: "2012-10-17" Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSCloudFormationFullAccess Policies: - PolicyName: !Sub CpCrUpdPolicyAiAgent-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Action: iam:PassRole Effect: Allow Resource: !GetAtt CodePipelineDeployCreateUpdateRoleAiAgent.Arn - Action: - s3:GetBucket* - s3:GetObject* - s3:List* Effect: Allow Resource: - !Sub arn:aws:s3:::${S3BucketArtifact} - !Sub arn:aws:s3:::${S3BucketArtifact}/* DependsOn: - CodePipelineDeployCreateUpdateRoleAiAgent - S3BucketArtifact # ------------------------------------------------------------# # EventBridge Rule for Starting CodePipeline # ------------------------------------------------------------# EventBridgeRuleStartCodePipelineAiAgent: Type: AWS::Events::Rule Properties: Name: !Sub ${SystemName}-${SubName}-aiagent-start-codepipeline Description: !Sub This rule starts pptx pdf converter CodePipeline for ${SystemName}-${SubName}. The trigger is the source code change in CodeCommit. EventBusName: !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:event-bus/default" EventPattern: source: - "aws.codecommit" detail-type: - "CodeCommit Repository State Change" resources: - !GetAtt CodeCommitRepoAiAgent.Arn detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - main RoleArn: !GetAtt EventBridgeRuleStartCpRoleAiAgent.Arn State: ENABLED Targets: - Arn: !Sub "arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${CodePipelineAiAgent}" Id: !Sub ${SystemName}-${SubName}-aiagent-start-codepipeline RoleArn: !GetAtt EventBridgeRuleStartCpRoleAiAgent.Arn DependsOn: - EventBridgeRuleStartCpRoleAiAgent # ------------------------------------------------------------# # EventBridge Rule Start CodePipeline Role (IAM) # ------------------------------------------------------------# EventBridgeRuleStartCpRoleAiAgent: Type: AWS::IAM::Role Properties: RoleName: !Sub EventBridgeStartCpRoleAiAgent-${SystemName}-${SubName} Description: !Sub This role allows EventBridge to start pptx pdf converter CodePipeline for ${SystemName}-${SubName}. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: !Sub EventBridgeStartCpPolicyAiAgent-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - "codepipeline:StartPipelineExecution" Resource: - !Sub "arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${CodePipelineAiAgent}" DependsOn: - CodePipelineAiAgent # ------------------------------------------------------------# # CodeBuild Project # ------------------------------------------------------------# CodeBuildProjectAiAgent: Type: AWS::CodeBuild::Project Properties: Name: !Sub ${SystemName}-${SubName}-aiagent Description: !Sub The build project for ${SystemName}-${SubName}-aiagent ResourceAccessRole: !GetAtt CodeBuildResourceAccessRoleAiAgent.Arn ServiceRole: !GetAtt CodeBuildServiceRoleAiAgent.Arn ConcurrentBuildLimit: 1 Visibility: PRIVATE Source: Type: CODEPIPELINE SourceVersion: refs/heads/main Environment: Type: ARM_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: "aws/codebuild/amazonlinux-aarch64-standard:3.0" ImagePullCredentialsType: CODEBUILD PrivilegedMode: true EnvironmentVariables: - Name: AWS_DEFAULT_REGION Type: PLAINTEXT Value: !Sub ${AWS::Region} - Name: AWS_ACCOUNT_ID Type: PLAINTEXT Value: !Sub ${AWS::AccountId} - Name: IMAGE_REPO_NAME Type: PLAINTEXT Value: !Ref EcrRepositoryAiAgent TimeoutInMinutes: 30 QueuedTimeoutInMinutes: 60 Artifacts: Type: CODEPIPELINE Cache: Type: NO_CACHE LogsConfig: CloudWatchLogs: GroupName: !Sub /aws/codebuild/${SystemName}-${SubName}-aiagent Status: ENABLED S3Logs: EncryptionDisabled: true Location: !Sub arn:aws:s3:::${S3BucketLogs}/codebuildBuildlog Status: ENABLED Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} DependsOn: - EcrRepositoryAiAgent - CodeBuildResourceAccessRoleAiAgent - CodeBuildServiceRoleAiAgent # ------------------------------------------------------------# # CodeBuild Resource Access Role (IAM) # ------------------------------------------------------------# CodeBuildResourceAccessRoleAiAgent: Type: AWS::IAM::Role Properties: RoleName: !Sub CbResourceAccessRoleAiAgent-${SystemName}-${SubName} Description: This role allows CodeBuild to access CloudWatch Logs and Amazon S3 artifacts for the project's builds. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - codebuild.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: !Sub CbResourceAccessPolicyAiAgent-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${SystemName}-${SubName}-aiagent" - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${SystemName}-${SubName}-aiagent:*" - Effect: Allow Action: - "s3:PutObject" - "s3:GetObject" - "s3:GetObjectVersion" - "s3:GetBucketAcl" - "s3:GetBucketLocation" Resource: - !Sub arn:aws:s3:::${S3BucketLogs} - !Sub arn:aws:s3:::${S3BucketLogs}/* # ------------------------------------------------------------# # CodeBuild Service Role (IAM) # ------------------------------------------------------------# CodeBuildServiceRoleAiAgent: Type: AWS::IAM::Role Properties: RoleName: !Sub CbServiceRoleAiAgent-${SystemName}-${SubName} Description: This role allows CodeBuild to interact with dependant AWS services. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - codebuild.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser Policies: - PolicyName: !Sub CbServicePolicyAiAgent-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - "codecommit:GitPull" Resource: !GetAtt CodeCommitRepoAiAgent.Arn - Effect: Allow Action: - "ssm:GetParameters" Resource: - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SystemName}_${SubName}_*" - Effect: Allow Action: - "s3:*" Resource: - !Sub arn:aws:s3:::${S3BucketArtifact} - !Sub arn:aws:s3:::${S3BucketArtifact}/* - !Sub arn:aws:s3:::${S3BucketLogs} - !Sub arn:aws:s3:::${S3BucketLogs}/* - Effect: Allow Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${SystemName}-${SubName}-aiagent" - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${SystemName}-${SubName}-aiagent:*" - Effect: Allow Action: - "codebuild:CreateReportGroup" - "codebuild:CreateReport" - "codebuild:UpdateReport" - "codebuild:BatchPutTestCases" - "codebuild:BatchPutCodeCoverages" Resource: - !Sub "arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/${SystemName}-${SubName}-aiagent*" DependsOn: - CodeCommitRepoAiAgent - S3BucketArtifact - S3BucketLogs まとめ いかがでしたでしょうか? とりあえず Amazon Bedrock AgentCore Runtime がデプロイされるところまで作りました。この後は React アプリ画面でエージェントとストリームレスポンスの会話ができるところまできちんと作り込もうと思います。 本記事が皆様のお役に立てれば幸いです。
こんにちは SCSKの庄司です。 今回は、ServiceNowにおける便利な小ワザをいくつか紹介していきます。 本記事は執筆時点(2026年1月)の情報になります。最新の内容は製品ドキュメントを参考にしてください。 小ワザ5選 1.「選択ボックスをルックアップ」カタログ変数で選択肢に複数のフィールドの値を表示させる 「選択ボックスをルックアップ」タイプのカタログ変数では、選択肢の値に複数のフィールドの値を使用できます。 インシデントテーブルを参照した変数で、番号と簡単な説明を選択肢に表示させてみます。 やり方は単純、ルックアップラベルフィールドでカンマ区切りでフィールド名を記入するだけです。 同姓同名のユーザーが存在する際などに、他のフィールドの値で差別化して正しい選択肢を選ばせることが出来ます。 2.「参照」カタログ変数で複数のフィールドの値を表示させる・検索する 同様に、「参照」タイプのカタログ変数で、変数の属性に以下のような値を入力すると、選択肢に複数のフィールドの値を表示できます。 ref_ac_columns=email;employee_number また、カンマで区切った後に以下も書き加えてみると、表示に使ったフィールドの値で検索することもできるようになります。 ref_ac_columns_search=true こちらも同姓同名のユーザーがいるとき等に役立ちます。 ちなみに、「選択ボックスをルックアップ」との差別化要因としては、内部の値にあります。 「選択ボックスをルックアップ」では「ルックアップ値フィールド」という項目で選択した値が内部で持たれます。 例えばインシデントテーブルの「番号」フィールドを「ルックアップ値フィールド」に選択すると、選択されたレコードの「番号」フィールドの値を内部的に保持します。 一方で、参照の時は選択されたレコードのsysIDが内部の値です。 スクリプトで利用する際などに違うので、値をどう利用したいかで使い分けるとよいと思います。 3.リストの「フィルタ」を右クリックしてクエリをコピー リスト画面でフィルタを実行した後、パンくずリスト上の任意の条件を右クリックすると下記の選択肢が表示されます。 この中尾の[クエリのコピー]を押下すると、指定した条件でのクエリをクリップボードにコピーすることが出来ます。 user_nameLIKEte^email!=NULL コピーしたクエリはこのままスクリプトなどで利用することが出来ます。 4.リストの「フィルタ」で中間にある特定の条件だけを削除 リスト画面でフィルタを実行した後、パンくずリスト上の条件から外したい任意の条件のすぐ左にある「>」にカーソルを合わせると、[次の条件を削除]という表記が出てきます。 このまま「>」を押下すると、直後の条件のみが削除できます。 いちいちフィルターをすべて開かずに削除できるので若干の時短になります。 5.テーブル名.~~ 左上のナビゲーションメニューに以下を入力してエンターを押下すると、それぞれ便利機能が使えます。 テーブル名.config : 該当テーブルのビジネスルール、通知、アクセス制御などをまとめた画面が開きます。 テーブル名.list : 該当テーブルをリスト表示。 テーブル名.form : 該当テーブルの新規レコード作成画面。 テーブル名.do : .formと同様の挙動。 なお、それぞれ大文字にすると新規タブでページが開きます(例:テーブル名.CONFIG) 特にconfigはそのテーブルに対してどのような処理が入っているかを一覧で確認できるので、使いどころによっては非常に便利かと思います。 config画面 まとめ 以上、Servicenowで使える小ワザでした。 どれも私が最近知ったものなので、意外と知らずにいる方も多いのかなと思って書いてみました。 是非知らないものがあれば参考にしてみてください。
前回の記事 では AWS DRS の設定までできたので、今回はリカバリ(フェイルオーバー)を実施したいと思います。 リカバリ(フェイルオーバー) リカバリ前準備 どのくらいリアルタイムで復旧できるのか確認するために、毎秒時刻をテキストファイルに出力させました。 リカバリ開始 「リカバリジョブを開始」ー「リカバリを開始」を押下します。 ※リカバリとリカバリドリルの挙動に違いはありませんが、リカバリジョブの履歴にそのジョブがドリルであったかリカバリであったかが記録されます。訓練 (ドリル) 目的であったのか実際のリカバリを実施したのかを区別するのに役立ちます。 復旧ポイントを選択します。 今回は最新のデータを選択しました。 リカバリジョブが開始されると「AWS Elastic Disaster Conversion Server」というサーバが起動しました。 Conversion Server は、AWS Elastic Disaster Recovery において、フェイルオーバー時にレプリケートされたデータを EC2 として起動可能な形に変換するための一時的なサーバのようです。通常時は起動しておらず、フェイルオーバー時のみ自動的に起動・停止されます。 「AWS Elastic Disaster Conversion Server」の停止後、リカバリインスタンスが起動してきました。 ジョブの実行履歴を確認すると20分ほどで復旧できたようです。 復旧したリカバリインスタンスはDRSコンソールからも確認できます。 リカバリインスタンスの確認 復旧したインスタンスの状態を確認します。 ホスト名とプライベートIPアドレスは復旧前後で変わりませんでした。 インスタンスタイプは起動設定で「インスタンスタイプの適切なサイジング」を指定しているため、自動的に割り当てられました。 ←ソースインスタンス(復旧前) →リカバリインスタンス(復旧後) 次に最後に出力された時刻を確認します。 リカバリジョブを開始してから2秒後の時刻まで書き込まれています。 ほぼほぼリアルタイムで復旧することができました。 実際の障害時には障害発生より前のポイント指定することになりますが、 達成したいRPOが存在する場合は、環境要因により変動するので事前検証をおすすめします。 フェイルバック検証 では、続いてフェイルバックを検証してみたいと思います。 今度はリカバリインスタンスを起点に東京リージョンに向けて、リバースレプリケーションを実施していきます。 DR先リージョン(今回はバージニア)のDRSコンソールからリカバリインスタンスを選択し、「リバースレプリケーションを開始」をクリックします。 バージニアリージョンのDRSコンソールを確認すると、東京リージョンへのレプリケーションが開始されました。 東京リージョンのDRSコンソールを確認すると、新しくソースサーバが登録されました。 ここはフルコピーとなるため、完了まで1時間半ほどかかりました。 データ量やネットワーク帯域によるので、実際に本番のフェイルバック時は考慮が必要です。 ソースサーバの登録が完了したら、リカバリジョブを実行すればサーバ復旧できます。 これ以降の流れはフェイルオーバーの時と同様のため、割愛します。 まとめ AWS Elastic Disaster Recovery(DRS)の操作は、ほぼすべてAWSマネジメントコンソール上で完結しており、専門的な手作業を求められる場面は多くありません。設定項目も整理されており、初見でも理解しやすい構成となっている点が印象的でした。 フェイルオーバー操作は数クリックで実行でき、あらかじめテストフェイルオーバーを実施することで、本番障害を想定した事前検証を容易に行えます。実際の切替手順を事前に確認できるため、障害発生時にも落ち着いて対応できる運用体制を構築しやすいと感じました。 また、DR専用のサーバや外部製品を常時用意する必要がなく、複雑なスクリプトや自動化処理を事前に組み込むことなくDR環境を構築できる点も大きな利点です。その結果、運用担当者に求められるスキルや習熟コストを抑えることができ、属人化しにくい運用が実現できます。 これらの点から、AWS DRS は 「DR を導入したいが、運用負荷や複雑さはできるだけ抑えたい」 というケースにおいて、非常に有効な選択肢であると感じました。
こんにちは、SCSKの齋藤です。 本記事では、AWSのパラメータシート作成を自動化するツールの バックエンド実装(Python) について解説します。 1.はじめに 本アプリの全体像や要件定義については、すでに以下の記事で紹介されています。 アプリ概要編 – ツールの全体像と目的 要件定義編 – 各AWSリソースの取得コマンドとシート分割ルール 「アプリ概要編」でも触れられている通り、AWSの設計書やパラメータシートを手作業でExcelにまとめる作業は、非常に時間がかかりミスも起きやすい工程です。 そこで私たちは、 AWS CLIの実行結果をExcelに自動変換するツール を開発しました。 本記事では、このツールの心臓部となる Pythonによるバックエンド実装 にフォーカスして、どうやって「リソースごとのバラバラなJSON構造」を「統一されたExcelフォーマット」に落とし込んだのか、その技術的な裏側をご紹介します。 2.作ったもの(バックエンド) AWSパラメータシート自動生成エンジン AWS CLIでリソース情報を取得し、Excelファイルに加工・出力するPythonアプリケーションです。 【処理フロー】 AWS CLI実行 → JSON取得 → 構造解析・整形 → Excel出力 Webフロントエンドからのリクエスト(リソースID等)を受け取り、以下の処理を自動で行います。 リソース別処理 : mapping.yaml の設定に基づき、リソースごとに最適なコマンドを発行 データ整形 : ネストされたJSONデータをExcelで見やすい形式に平坦化・展開 Excel生成 : 表紙・改訂履歴・各リソースごとのシートを出力 ※なお、Web画面(フロントエンド)の実装については、次回の記事で詳しく解説されます。 3.システム構成 開発期間 だいたい 1ヶ月半 くらいかけて作りました。 最初の2週間は基本的な機能を作って、残りの期間でリファクタリングや機能追加をしていった感じです。本業の合間に少しずつ進めていたので、実際の作業時間としては 30~40時間 くらいだと思います。 開発環境 マシン : Ubuntu (WSL2) エディタ : VS Code + AI 言語 : Python 3.12 パッケージ管理 : uv VS CodeのAIコード補完には本当に助けられました。「こういう処理を書きたい」と思ったときに、コメントを書くだけでコードを提案してくれるので、Python初心者の私でもなんとか書き進めることができました。 全体の流れ main.py(エントリーポイント) Step 1: AWS CLI実行 → Step 2: JSON収集 → Step 3: Excel生成 ディレクトリ構成 generate_parameter_sheet/ ├── config/ │ ├── mapping.yaml # ← ここで取得するリソースを定義 │ └── request.json # ← 取得対象のリソースID ├── src/ │ ├── main.py # エントリーポイント │ ├── aws_executor.py # AWS CLI実行 │ ├── data_processor.py # データ変換 │ ├── excel_generator.py# Excel生成 │ └── excel_formatter.py# スタイル適用 ├── input/ # AWS実行結果(JSON) └── output/ # 出力Excel 4.使用技術 項目 技術 言語 Python 3.12 パッケージ管理 uv Excel操作 openpyxl 設定ファイル YAML AWS操作 AWS CLI なぜこの構成にしたか Python : 学習コストが低く、ライブラリが豊富。ネットで調べると情報がたくさん出てくる uv : 依存関係管理が簡単( uv sync 一発)。pipより速くて気持ちいい openpyxl : PythonでExcelを扱う定番ライブラリ。ドキュメントも充実している YAML : 設定ファイルとして読みやすい。JSONより人間に優しい 正直、最初は「とりあえず動けばいいや」くらいの気持ちで技術選定しました。でも結果的に、この構成は初心者にも扱いやすくて正解だったと思います。 5.対応しているAWSリソース 現在、以下のリソースなどに対応しています: EC2 / VPC / ELBv2 / RDS / Route53 / S3 / ACM / CloudWatch など 各リソースでどのようなAWS CLIコマンドを使用しているか、シートを分割するかまとめるか、複数コマンドを結合するかなどの詳細な要件については、以下の記事をご覧ください: 要件定義編:各AWSリソースの取得ルールとシート分割設計 出力例 consolidated_aws_resources.xlsx ├── 表紙 ├── 改訂履歴 ├── vpcs # VPC情報 ├── web-server-01 # EC2(Tags.Nameから) ├── batch-server # EC2(Tags.Nameから) ├── prod-alb # ELB(ロードバランサー名から) ├── example.com # Route53(ドメイン名から) ├── prod-database # RDS(DBインスタンス名から) └── ... 6.工夫した点 汎用化への挑戦 このツールを作る上で一番大変だったのが、 リソースごとに要件がバラバラ だったことです。 【リソースごとの違い】 EC2 : 複数インスタンス → 各インスタンスごとにシート分割 VPC : 複数VPC → 1つのシートにまとめて出力 ELB : ALB + ターゲットグループ + 属性 → 結合して1シート RDS : DB + サブネット + パラメータ → 結合して1シート Route53 : ホストゾーン + レコード → 結合して1シート 同じ「AWSリソースをExcelに出力する」という処理なのに、 シート分割の単位が違う (リソースごとに分ける or まとめる) 複数のAWS CLIコマンドを結合する必要がある (ELBやRDS) JSON構造が全然違う (EC2は Reservations[].Instances[] 、RDSは DBInstances[] ) シート名に使うフィールドが違う (EC2は Tags.Name 、RDSは DBInstanceIdentifier ) これらを すべてコードで分岐させていたら、メンテナンス不可能 になっていたと思います。 そこで、 mapping.yaml という設定ファイルで すべての違いを吸収できる設計 にしました。 設定ファイルによる制御 mapping.yaml に設定を追加するだけで、新しいAWSリソースに対応できるようにしました。 mapping.yamlの主要オプション: オプション 説明 data_structure JSONのどの階層からデータを抽出するか(例: Reservations[].Instances[] ) sheet_name_field シート名として使用するフィールド(例: Tags.Name ) merge_subfolder_files サブフォルダのJSONファイルを結合するかどうか json_transform 文字列フィールドをJSONとしてパースするなどの変換処理 allow_batch 複数リソースをまとめて取得するかどうか # 例:RDSの設定 rds_db_instances: description: "RDS DBインスタンス情報取得" allow_batch: false merge_subfolder_files: true sheet_name_field: "DBInstanceIdentifier" # ← シート名に使うフィールド commands: - command: "aws rds describe-db-instances --db-instance-identifier {values}" is_main_data: true - command: "aws rds describe-db-subnet-groups ..." merge_into_main: "DBSubnetGroups" コードを書かなくても、YAMLを編集するだけで拡張できる のがポイントです。 新しいリソースを追加するときも、既存の設定をコピーして少し変えるだけ。最初からこの設計にしていればよかったのですが、途中で気づいてリファクタリングしました。 シート名の自動決定 各リソースのシート名を、意味のある名前で自動設定するようにしました。 リソース シート名の決定方法 EC2 Tags.Name (例:web-server-01) ELB LoadBalancerName (例:prod-alb) RDS DBInstanceIdentifier (例:prod-database) Route53 HostedZone.Name (例:example.com) 7.苦労した点 JSONの構造がリソースごとに違う EC2は Reservations[].Instances[] という構造、RDSは DBInstances[] という構造…と、AWSのレスポンス形式がバラバラで苦労しました。 解決策: mapping.yaml に data_structure オプションを追加し、設定で対応できるようにしました。 ec2_instances: data_structure: "Reservations[].Instances[]" # ← 構造を定義 このオプションを使って、JSONから必要なデータを抽出する処理を実装しました: def extract_data_by_structure(json_data, structure): """data_structure設定に基づいてJSONからデータを抽出""" if not structure: return json_data # "Reservations[].Instances[]" のような形式をパース parts = structure.replace('[]', '').split('.') result = json_data for part in parts: if isinstance(result, list): # リストの場合は各要素から抽出 result = [item.get(part, []) for item in result] # ネストしたリストをフラット化 result = [item for sublist in result for item in (sublist if isinstance(sublist, list) else [sublist])] elif isinstance(result, dict): result = result.get(part, []) return result これにより、どんな構造のJSONでも設定ファイルに書くだけで対応できるようになりました。 複数コマンドの結果を結合する処理 RDSの場合、DBインスタンス情報だけでなく、サブネットグループやパラメータグループの情報も一緒に取得して結合する必要がありました。 # mapping.yamlでの設定例 rds_db_instances: commands: - command: "aws rds describe-db-instances ..." is_main_data: true # メインのデータ - command: "aws rds describe-db-subnet-groups ..." merge_into_main: "DBSubnetGroup" # メインに結合 - command: "aws rds describe-db-parameter-groups ..." merge_into_main: "DBParameterGroups" # メインに結合 結合処理のロジックは以下のようになっています: def merge_subcommand_results(main_data, sub_data, merge_key): """サブコマンドの結果をメインデータに結合""" if isinstance(main_data, dict): main_data[merge_key] = sub_data elif isinstance(main_data, list): # リストの場合は各要素に結合 for item in main_data: if isinstance(item, dict): item[merge_key] = sub_data return main_data Excelシート名の31文字制限 Excelのシート名は31文字までという制限があり、長いリソース名が切れてしまう問題がありました。 解決策: シート名を自動で31文字以内に切り詰める処理を追加しました。また、 [] などExcelで使えない文字も自動で置換するようにしています。 def sanitize_sheet_name(name, max_length=31): """Excelシート名として使用できる形式に変換""" # 使用できない文字を置換 invalid_chars = ['\\', '/', '*', '?', ':', '[', ']'] for char in invalid_chars: name = name.replace(char, '_') # 長さを制限 if len(name) > max_length: name = name[:max_length-3] + '...' return name シート名の動的決定 EC2は Tags.Name 、RDSは DBInstanceIdentifier というように、リソースごとにシート名として使いたいフィールドが異なります。これを mapping.yaml の sheet_name_field で指定できるようにしました。 def get_sheet_name(data, config): """設定に基づいてシート名を決定""" sheet_name_field = config.get('sheet_name_field') fallback = config.get('sheet_name_fallback', 'unknown') if not sheet_name_field: return fallback # ネストしたフィールドにも対応(例:Tags.Name) value = data for key in sheet_name_field.split('.'): if isinstance(value, dict): value = value.get(key) elif isinstance(value, list): # Tags配列から特定のKeyを探す場合 for item in value: if item.get('Key') == key: value = item.get('Value') break else: break return sanitize_sheet_name(str(value)) if value else fallback 8.学んだこと プログラミングは「問題解決の手段」 最初は「Pythonを勉強しよう」と思っていましたが、実際に作ってみると 「この問題を解決したい」→「そのためにはこう書けばいい」 という流れで学習が進みました。 目的があると、学習効率が全然違いますね。「Python入門」みたいな本を読むより、実際に何か作りながら学ぶ方が頭に入ってくる気がします。 設計の重要性 最初は場当たり的にコードを書いていましたが、途中で「これじゃ拡張できない…」と気づき、設定ファイル(YAML)で制御する形にリファクタリングしました。 最初からちゃんと設計していれば… という反省があります。 実際、後半は「新しいAWSリソースを追加したい」となったときに、YAMLに数行追加するだけで対応できるようになりました。最初からこの設計にしていれば、開発期間はもっと短くなったと思います。 小さく始めて、少しずつ育てる 最初から完璧なものを作ろうとすると、たぶん挫折していました。 まずは 「EC2の情報をExcelに出力する」 だけの簡単なものを作って、動くことを確認してから、少しずつ機能を追加していきました。 Step 1: EC2だけ対応 Step 2: VPCを追加 Step 3: ELBを追加(複数コマンドの結合が必要) Step 4: RDSを追加(動的サブクエリが必要) Step 5: リファクタリング(設定駆動型に) この「小さく作って、育てる」アプローチは、プログラミング初心者には特におすすめです。 AIの活用 正直、このツールはAIの助けがなければ完成しなかったと思います。 エラーメッセージの意味を教えてもらう 「こういうことがしたい」と伝えると実装方法を提案してもらう コードレビューをしてもらう 「もっとシンプルに書けない?」と聞くとリファクタリング案を出してくれる プログラミング経験が浅くてもAIを活用すれば、ここまで作れる時代になったんだな と実感しました。 特に、エラーが出たときに「このエラーは何?」と聞くと、原因と解決策を教えてくれるのが本当に助かりました。以前なら数時間かけてググっていたような問題が、数分で解決できるようになりました。 9.フロントエンドとの連携 このバックエンドツールは、 Webフロントエンドと連携 して動作します。 ユーザーがWeb画面で取得したいリソースを選択すると、フロントエンドからバックエンドにリソースIDなどのパラメータが渡され、このPythonツールが実行される仕組みになっています。 【Webフロント】 → 【バックエンド(本記事)】 → 【Excel出力】 リソース選択・実行ボタン パラメータ受信・Python実行 Excel生成・ダウンロード 関連記事 アプリ概要編 – ツールの全体像と目的 要件定義編 – 各AWSリソースの取得ルール フロントエンド編 – Web画面の実装 10.今後やりたいこと より多くのAWSリソースに対応(Lambda、DynamoDB、CloudFrontなど) 設定値の差分比較機能(前回との変更点を可視化) パラメータシートからAWSリソースを作成する逆方向の機能 Terraformとの連携 日本語対応(パラメーター名など) 視覚的にさらに見やすく(罫線やフォーマットの改善) 11.まとめ プログラミング経験が浅い私でも、約1ヶ月半かけて 実用的なツール を作ることができました。 数字で見る効果 項目 Before(手作業) After(自動化) 作業時間 2〜3時間 20〜30秒 コピペミス 発生する なし 設定変更時 手動更新 再実行するだけ 最後に 「面倒だな」と思ったことを自動化できると、本当に気持ちいいです。 そして、一度作ってしまえば 何度でも使い回せる のがプログラミングの良いところ。最初は大変でしたが、今では「作ってよかった」と心から思います。 プログラミングに自信がない方でも、 「解決したい課題」 があれば、ぜひチャレンジしてみてください!AIを活用すれば、想像以上にいろいろなことができますよ。 参考 openpyxl公式ドキュメント AWS CLI コマンドリファレンス uv – Python パッケージマネージャー 次回は、 加藤さん による「フロントエンド編」の記事です。Web画面の実装について詳しく紹介される予定ですので、ぜひお楽しみに!
こんにちは。SCSKの井上です。 この記事では、New RelicのInfrastructureエージェントの導入方法について解説します。手順を説明する前に、まずInfrastructureエージェントを導入すると何ができるのか、リソース消費は増えないのかなど、よくある疑問に触れていますので、理解を深めてから実際の手順に挑んでいきます。 はじめに New Relic Infrastructureは、オンプレミスサーバやクラウド環境の基盤を観測するための機能を提供 します。この機能により、CPU、メモリ、ディスクI/O、ネットワーク、プロセスなどのリソースをリアルタイムで可視化できます。ハードウェアの性能を見える化することで、インフラに関する問題を迅速に発見し、対応することが可能です。 利用には、観測対象のサーバに New Relic Infrastructure エージェントをインストールする必要 があります。エージェントは常駐プロセスとしてシステム観測に必要となるテレメトリデータを収集し、New Relicプラットフォームへ送信します。エージェントをインストールしただけでは、New Relicへログは転送されません。ログを New Relic に集約することで、各サーバーにログインせずに一元管理が可能になります。これにより、障害調査の迅速化や、サーバーログ調査の属人化解消など、運用効率が向上し工数削減につながります。この記事では導入に必要な手順を解説していきます。 Infrastructureモニタリングの概要 | New Relic Documentation New Relic provides flexible, dynamic monitoring of your entire infrastructure. Learn about features, installation, and u... docs.newrelic.com 事前準備 すでにNew Relicアカウント、ライセンスキー、ユーザーキーをすでに発行済の前提で記載しています。発行の手順については、過去の記事をご参考いただけますと幸いです。この記事での導入対象はamazon linux2023としています。 【New Relic】New Relicでユーザーを作成する方法 この記事では、New Relicのユーザー作成過程において有償ユーザーと無償ユーザーの違い、カスタムロールの作成方法についても解説します。 blog.usize-tech.com 2025.12.04 【New Relic】エージェント導入前に知っておくべき設定鍵の種類と役割 New Relicを導入する際には、いくつかの鍵を正しく設定する必要があります。この記事では、ライセンスキーとユーザーキーの概要、用途、発行手順、そしてセキュリティを確保する管理方法を理解するための参考になれば幸いです。 blog.usize-tech.com 2025.12.10 エージェントの動作環境 New Relic Infrastructure エージェントを導入にあたりシステム要件があります。下記は、執筆当時のものになりますので、最新版は下部の公式サイトをご確認ください。 カテゴリ 詳細 対応アーキテクチャ 最新情報は下記、公式サイトをご参照ください。 対応OS(Linux/Windows/macOS) 最新情報は下記、公式サイトをご参照ください。 権限 Linux :root推奨 Windows:Administrator必須 ホスト名要件 固有であること(localhost不可) ネットワーク要件 New Relicへのアウトバウンドデータ送信にHTTPS(443)を使用。受信ポートは開く必要無し。 TLS1.2以上の通信 コンテナ対応 Kubernetes/ECS/EKS対応。最新情報は下記、公式サイトをご参照ください。 設定管理ツール Ansible、Chef、Puppet、Elastic Beanstalk対応 リソース消費 軽量(詳細は、次章に記載) infrastructureエージェントの互換性と要件 | New Relic Documentation Compatibility information for infrastructure monitoring, including supported Linux and Windows versions. docs.newrelic.com ファイアウォールやセキュリティポリシーで外部との通信制限がされている場合は、New Relicエージェントがデータ送信できるようにドメインやエンドポイントを追加する必要があります。 New Relicネットワークトラフィック | New Relic Documentation Network connections used by New Relic for sending and receiving data: IP addresses, domains, ports, endpoints. docs.newrelic.com Infrastructure エージェント導入によるリソース消費を知る New Relic Infrastructure エージェントを入れると、システム負荷が増えることが予想されます。以下、New Relicが公開している情報です。エージェント自体は軽量ですが、監視対象が増えると収集・処理のためのリソースが比例して増えますので、 まずはテスト環境等に導入して様子を見る ほうが良いですね。 ホストタイプ ベース環境 CPU使用率 仮想メモリ 常駐メモリ ディスク使用量 Linuxシングルタスク EC2 約0.3% 約1GB 約25~35MB 約50MB Linux Docker EC2 (CentOS7, 25コンテナ/100プロセス) 約0.8% 約1GB 約25~35MB 約50MB インフラエージェントのオーバーヘッド | New Relic Documentation The New Relic infrastructure agent is designed to minimize its performance impact. This document outlines resource usage... docs.newrelic.com Infrastructureエージェントを導入することで何ができるの? Infrastructure エージェントを導入することで、サーバの負荷や異常なプロセスの検出、リソースの障害検知・アラートを通知、新しいアプリケーションやサービスのデプロイ後に、インフラストラクチャの健全性を確認し、リソース高騰等の問題がないかをチェックすることができます。 どんなデータが収集されるの? 実際にどんなデータがどれくらいの間隔で収集されるのかを下記に整理しました。収集間隔はデフォルト値のため、設定ファイルにて変更することはできます。名前がSampleとついているのは、一定間隔で取得したサンプル値を表すためになります。New relicでは導入されているシステムへの負荷を抑えるため、例えば5秒ごとにCPU使用率のデータを取得する場合、その5秒間目の値をサンプルとして保存しています。 イベント名 説明 参照 SystemSample CPU、メモリ、ディスク、ネットワークなど、サーバー全体の状態を5秒ごとに記録 New Relic data dictionary | New Relic Documentation ProcessSample 各アクティブプロセスに関して、20秒ごとに記録 New Relic data dictionary | New Relic Documentation StorageSample マウントされた単一のストレージデバイスを20秒ごとに記録 New Relic data dictionary | New Relic Documentation NetworkSample ネットワークデバイスのインタフェースおよびアドレス情報、使用量データを10秒ごとに記録 New Relic data dictionary | New Relic Documentation ContainerSample 各DockerコンテナのID、名前、イメージ、イメージ名、またCPU、メモリ、ネットワークに関するデータを15秒ごとに記録 New Relic data dictionary | New Relic Documentation InfrastructureEvent 構成情報またはシステム状態が追加/削除/変更されると、そのアクティビティ発生時に記録 New Relic data dictionary | New Relic Documentation 上記のデータ以外にもインフラ基盤から出力されるログをNew Relicに送ることができます。ただし、 エージェントをインストールしただけでは、ログは転送されません ので、明示的にどのログを転送したいかを設定ファイルに記載する必要があります。 デフォルトのインフラストラクチャモニタリングデータ | New Relic Documentation A list of the default events and attributes used by New Relic to collect and store your infrastructure monitoring and ho... docs.newrelic.com データ(ログ)送信の仕組み Infrastructure エージェントをインストール後、Infrastructure エージェントは、/etc/newrelic-infra/logging.d/ に配置された yml ファイルを Fluent Bit の設定に変換し、Fluent Bit がログの送信を実施します。そのため、Infrastructure エージェントをインストールした際に、Fluent-bitのプロセスも常駐します。転送経路はTLSで暗号化されており、ログはNew Relicのログエンドポイントに送信された後、保存時にも暗号化されます。これにより、ログデータは送信中も保存中も安全に保護されます。ログ転送については、Fluent-bit以外にも対応しています。メトリクスデータについては、エージェントから直接New Relicのプラットフォームに送信しています。 ログをNew Relicへ送信する前にログに個人情報や認証に関わるデータが出力されていないかを確認する必要があります。New Relicのプラットフォームはデータ転送は暗号化され、データ保存はセキュアな環境で管理されていますが、個人情報や機密データはNew Relicに送信しないようログ送信対象を除外などの対処する必要があります。 New Relicへのログ転送 | New Relic Documentation How to forward your logs into New Relic so you can use enhanced log management capabilities. docs.newrelic.com Data privacy with New Relic | New Relic Documentation Links to detailed information about how New Relic protects you and your customers' data privacy. Also see our security w... docs.newrelic.com Infrastructure エージェントのインストール インストール手順にはガイドを利用したインストール方法とパッケージを指定してインストールする方法があります。この記事ではガイドに沿ってインストールしますので、導入についての難易度は低めです。10分-15分程度でインストールは完了します。インストール後のNew Relicの使い方や見方については別記事にて解説していきます。 1.左上の「Integrations & Agents」をクリックし、「Guided install」を選択します。 2.導入対象を選択します。ここではLinuxを例に進めます。 3.「 Use an existing key 」をクリックし、ユーザーキーを入力します。忘れた場合や保持していない場合は、Create a new keyをクリック後に、自動生成されます。 4.サーバーに実行するコマンドが表示されますので、「Copy to Clipboard」をクリックします。プロキシサーバーを使っている場合は、Use a proxyのトグルをクリックし、入力します。タグについてはブランクで問題ありません。使い方については別途ご紹介します。 5.対象サーバにログインし、項番4でコピーしたコマンドを管理者権限で実行します。実行後、以下のような画面が表示されますので、5分程度待ちます。 6.以下が表示されていることを確認します。 Infrastructure Agent (installed) Logs Integration (installed) 7.項番4の画面にて「Continue」をクリックすると、エージェントとNew Relicプラットフォームとの通信確認が行われます。赤枠の表示となっていてれば、問題ありません。「See your data」をクリックします。 8.エージェントをインストールしたサーバのメトリクス情報が表示されます。これで手順は完了です。 Linux向けinfrastructureエージェントをインストールする | New Relic Documentation Instructions for how to install New Relic infrastructure monitoring for Linux systems using a package manager. docs.newrelic.com Infrastructure エージェントの設定ファイル Infrastructure エージェントをインストール後、ymlが作成されます。エージェントのログ出力レベルなど、エージェントの動作を制御するため設定はymlファイルに記述しますが、 yml設定ファイルよりも環境変数の値が優先されます。 以下は、Infrastructure エージェントインストール時に作成されます(他にもある可能性があります)。 ファイル名 役割 デフォルトパス(Linux) 備考 newrelic-infra.yml Infrastructure Agent の基本設定 /etc/newrelic-infra.yml エージェント起動時に読み込まれる。記載後、エージェント再起動必要 logging.yml ログ転送設定 /etc/newrelic-infra/logging.d/logging.yml 記載後、エージェント再起動不要 integrations.d/*.yml 各種インテグレーション設定 /etc/newrelic-infra/integrations.d/ サービスごとに配置 目的に応じて、設定ファイルの編集箇所や内容は異なります。この記事で解説するファイルは2つになります。 newrelic-infra.yml:New Relic Infrastructure エージェントを制御する設定ファイル logging.yml :New Relic Infrastructure エージェントでログ送信を設定するためのファイル デフォルトで”newrelic-infra.yml”に記載されている内容 ガイドに従ってインストールした場合、以下の内容が記述されています。それぞれどのような意味を持つのかを説明します。プロセス情報の収集はGuided installでインストールした場合、デフォルトは有効(true)で起動しています。 記載場所:/etc/newrelic-infra.yml 設定値 内容 enable_process_metrics: true OS上で動作しているプロセスのCPU、メモリ使用量などの情報をNew Relicに送信 status_server_enabled: true HTTPで自身のエージェントの状態を確認 ガイド付きインストールを使用する場合、デフォルトで有効。 status_server_port: 18003 上記を実施するためのポート番号 license_key: ***** 記載したキーで紐づけられているアカウントにデータが送信 custom_attributes: nr_deployed_by: newrelic-cli New Relic CLIを使ってデプロイされたことを示すカスタム属性 CLI が自動的に付与するメタ情報 newrelic-infra.ymlのテンプレートは、以下で公開されています。 infrastructure-agent/assets/examples/infrastructure/newrelic-infra-template.yml.example at master · newrelic/infrastructure-agent · GitHub エージェントのメトリクスデータ取得間隔の変更 サンプリング頻度が高いと、New Relicへの送信データ量が増え、コストやネットワーク帯域に影響を与える可能性があります。メトリクスデータの取得間隔を変更したい場合、以下のサンプルの値を記載することで、変更することができます。設定変更後はエージェントの再起動が必要です。 記載場所:/etc/newrelic-infra.yml 設定値 最小値 (単位:秒) metrics_network_sample_rate 10 metrics_process_sample_rate 20 metrics_storage_sample_rate 5 metrics_system_sample_rate 5 インフラストラクチャエージェントの設定 | New Relic Documentation Configuration settings for the New Relic infrastructure agent, including YAML and environment variable names, value, min... docs.newrelic.com エージェントログレベルの変更とログローテーション エージェントログのデフォルトレベルはINFOですが、WARN以上のみ出力したい場合や、障害調査でDEBUGレベルの詳細を確認したい場合には、出力レベルを変更できます。設定はnewrelic-infra.ymlに記載し、変更後はエージェントの再起動が必要です。また、ログを分割しないと肥大化してディスクを圧迫するため、分割サイズや保持世代数の設定を推奨します。 記載場所:/etc/newrelic-infra.yml log: level: INFO #ERROR/WARN/INFO/DEBUG file: /var/log/newrelic-infra/newrelic-infra.log rotate: max_size_mb: 100 #100MBを超えたらローテーション(設定値は例) max_files: 10 #最大10世代分のログを保持(設定値は例) compression_enabled: true #古いログを圧縮して保存 file_pattern: YYYY-MM-DD_hh-mm-ss.log #ローテーション時のファイル名パターン(設定値は例) インフラストラクチャエージェントの設定 | New Relic Documentation Configuration settings for the New Relic infrastructure agent, including YAML and environment variable names, value, min... docs.newrelic.com インフラストラクチャ・エージェントのトラブルシューティング用ログの生成 | New Relic Documentation Enable verbose logging or smart verbose mode for the New Relic infrastructure agent, then collect about 3 to 5 minutes w... docs.newrelic.com サーバーログ送信設定 アプリケーションログ、セキュリティ関連ログ、システムログなど、サーバー内でエラーが出力されていても、New Relicに送信しなければ、一つ一つサーバーにログインして確認しなければなりません。 New Relicのコンソール画面でエージェントを導入したサーバのログを見たい場合、転送するログをymlに記載する必要があります。 設定ファイルは以下になりますが、環境によっては格納場所が異なる可能性があります。デフォルトで送信対象のログが記載されていますが、不要な場合は削除しても問題ありません。この変更については、エージェントの再起動は不要です。 記載場所:/etc/newrelic-infra/logging.d/logging.yml 記載例は以下になります。attributes以下のラベル名(ここではservice,env)は自由にカスタマイズできますが、デフォルトで用意されているラベルがあります。 logs: - name : logging.yml内においてどのログについての設定か、識別のみに用いる file : ここで指定されたパスのログファイルがNew Relicに転送 attributes : ログに関連付ける追加情報を指定 (この行は何も書かない) service : ログ検索の際にキーワードで検索するラベル例 (apache,httpd) env : ログ検索の際にキーワードで検索するラベル例 (prd,stg) ・・・ logtypeで設定したキーワードが赤枠部分に表示されます。検索を行う際、デフォルトで用意されている「Attributes」カテゴリも活用することで、目的のログを素早く絞り込むことができます。ただし、設定反映後以降のみ対象となりますので、過去にさかのぼって反映はすることができません。デフォルトで用意されているラベルもここから確認することができます。 インフラストラクチャエージェントを使用してログを転送する | New Relic Documentation How to forward your logs to New Relic using our infrastructure agent, so you can use enhanced log management capabilitie... docs.newrelic.com ログに個人情報や認証に関わるデータが出力されていないかを確認する必要があります。データ転送は暗号化され、データ保存はセキュアな環境で管理されていますが、個人情報や機密データはNew Relicに送信しないよう設定する必要があります。 Data privacy with New Relic | New Relic Documentation Links to detailed information about how New Relic protects you and your customers' data privacy. Also see our security w... docs.newrelic.com エージェントのログファイル Infrastructureエージェントを導入したにもかかわらず、New Relicのコンソール画面にホストが表示されない、またはメトリクスが更新されない場合、エージェントの動作や通信に問題がある可能性があります。その際は、以下のログに記録された内容を確認してください。 /var/log/newrelic-infra/newrelic-infra.log インフラストラクチャ・エージェントのトラブルシューティング用ログの生成 | New Relic Documentation Enable verbose logging or smart verbose mode for the New Relic infrastructure agent, then collect about 3 to 5 minutes w... docs.newrelic.com Infrastructure エージェントの動作プロセス エージェントをインストールした後、サーバーには以下のプロセスが動作しています。サーバーのリソースが高騰している場合、以下のプロセスについて確認します。必要に応じて収集対象や間隔を見直します。 プロセス名 意味 用途 newrelic-infra-service サービス管理プロセス エージェントの起動・停止 newrelic-infra エージェントプロセス メトリクス、プロセスの収集・転送、ログ転送 エージェントの再起動はこのプロセスを実施 fluent-bit ログ転送プロセス ログを収集・転送 インフラストラクチャ・エージェントの動作 | New Relic Documentation Standard New Relic's infrastructure agent behavior at startup, retry, maintenance, shutdown, etc. docs.newrelic.com Infrastructure エージェントのリトライ処理 CPUやメモリなどのメトリクス情報は送信失敗した時点で破棄されますが、設定変更やパッケージのインストール・更新などの構成情報は、送信が失敗しても通信が回復した後でNew Relicに送信 されます。短時間に大量のリトライが起きるとシステムへの負荷がかかるため、再送は再試行間隔を伸ばすことでシステム負荷を軽減する”バックオフパターン”に従って行われます。 インフラストラクチャ・エージェントの動作 | New Relic Documentation Standard New Relic's infrastructure agent behavior at startup, retry, maintenance, shutdown, etc. docs.newrelic.com Infrastructure エージェントの動作サポート期限 メーカーがOSのサポートを終了するまで、New Relicにおいてもエージェント動作をサポートします。パッケージはサポート終了(EOL)後のシステムでも利用できる場合がありますが、動作保証はありません。New Relic Infrastructureエージェントを含め、日々新機能が追加されています。新機能を利用するには、エージェントのバージョンアップが必要です。New Relicでは、3か月ごとのInfrastructureエージェントのバージョンアップを推奨しています。自動でバージョンアップはされませんので、リリースノートを確認し、定期的なバージョンアップを計画することが求められます。 Infrastructureエージェントを更新する | New Relic Documentation Update the New Relic infrastructure agent on Linux, Windows, or macOS systems. docs.newrelic.com インフラ監視エージェントのEOLポリシー | New Relic Documentation Policies, start and end dates for support of New Relic infrastructure agent releases. docs.newrelic.com Infrastructure agent release notes | New Relic Documentation Infrastructure agent release notes docs.newrelic.com さいごに 本記事では、New Relic Infrastructureでできること、収集できる情報、インストール手順からログ送信設定について解説しました。導入は意外と簡単だと感じた方も多いのではないでしょうか。ログを収集・送信するためには、設定ファイルに専用の記述を追加し、ログ転送機能を有効化する必要があります。これにより、システムログやアプリケーションログを効率的に監視し、トラブルシューティングやパフォーマンス分析に活用できます。次回の記事では、Infrastructure エージェントの具体的な活用方法について解説します。 SCSKはNew Relicのライセンス販売だけではなく、導入から導入後のサポートまで伴走的に導入支援を実施しています。くわしくは以下をご参照のほどよろしくお願いいたします。
ALBのターゲットとして AWS Lambda 関数を設定し、バックエンドへのある種のプロキシとして使用する場合がありました。 Lambda関数の制約としては実行時間15分の制限などは有名ですが、 ALBのターゲットとして使う場合に注意すべき設定について 、少しつまづいたので整理してみました。 ペイロードサイズの制約について ALB-Lambdaのリクエストおよびレスポンスのペイロードのサイズ上限は 1MB となります。 通常のLambdaの場合、同期呼び出しの場合は6MB、非同期呼び出しの場合は1MB ですので、同期呼び出しと比較するとかなりの制限に感じます。 バックエンド処理によっては、ペイロードが1MBを超える可能性は多いにあります。(例:ファイルアップロード処理など) この制限は 緩和不可能 のため、ALB-Lambdaを使用する場合は、設計段階でペイロードサイズの上限=1MB については認識しておく必要があります。1MBを将来的にせよ超える可能性のある場合はLambda以外の手段を検討する必要があると思います。 代替としては、EC2やECSでproxy を実装するより他無いと思っています。 (* 正確にはリクエストに対してはリクエストボディのサイズに対する制限、レスポンスに対してはLambdaが出力するJSONのサイズに対する制限のようです。) 複数値ヘッダーの使用について バックエンドとやり取りするヘッダーに複数値のものがある場合、 ターゲットグループで「複数値のヘッダー」をオンにする設定が必要です。 この設定を欠いた場合は、 複数値のうちの最後の値のみ使用される 、という仕様があります。 具体的にはCookieの使用が分かりよいでしょう。 例えば、クライアントからALB-Lambdaへのリクエストクッキーに name1=val1/name2=val2 という二つのクッキーを含んでいたとしましょう。 ヘッダーの観点から見ると、 : "cookie":"name1=val1", "cookie":"name2=val2", : のように、”cookie”ヘッダの値として、”name1=val1″ , “name2=val2″ と二つの値を持つケースです。 複数値ヘッダーをオフの状態の場合は以下のように最初の値が欠落してLambdaにわたります。多くの場合、意図した動作にならないでしょう。仕様としては、単一ヘッダの複数値は”最後の値のみが渡される” です。 また、Lambdaが受け取るイベントの形式は「複数値のヘッダー」のオン・オフで異なるものになるため、Lambdaのロジックに影響があります。詳細は以下のドキュメントに詳しいです。 Lambda 関数を Application Load Balancer のターゲットとして使用する - ELB Lambda 関数をターゲットとして Application Load Balancer に登録する方法について説明します。 docs.aws.amazon.com まとめ 調べるとドキュメントには書いていることではありますが(どんなナレッジもそうでしょうが)、そもそもそういう固有の制約があるということに考えが至らず、少し引っ掛かりました。聞いたことある程度でも頭に残っていれば、事前に調べる対応もとれたなと思っています。 みなさんの頭の片隅に欠片でもこの内容が残っていれば幸いです。
SCSKの畑です。 今回は小ネタですが、アプリケーション開発中に少し戸惑った内容であることと、(あまりにも当たり前すぎて?)Web 上にあまり情報が見つけられなかったことも相まってエントリとして残しておこうと思っての投稿です。 小ネタ本題 先般投稿した一連のエントリの通り、アプリケーションのバックエンドからフロントエンドまで広く手を入れることになりましたが、その際に Amplify・AppSync 周りで遭遇した内容となります。 AWS AppSync の制約を回避するためのアプリケーション改修その1:概要編 昨年度リリースした Web アプリケーションにおける AppSync の制約を回避するためのアプリケーション改修について説明します。第1回は概要編です。 blog.usize-tech.com 2026.01.07 AWS AppSync の制約を回避するためのアプリケーション改修その2:バックエンド編 昨年度リリースした Web アプリケーションにおける AppSync の制約を回避するためのアプリケーション改修について説明します。第2回はバックエンド編です。 blog.usize-tech.com 2026.01.07 AWS AppSync の制約を回避するためのアプリケーション改修その3:フロントエンド編 昨年度リリースした Web アプリケーションにおける AppSync の制約を回避するためのアプリケーション改修について説明します。第3回はフロントエンド編です。 blog.usize-tech.com 2026.01.07 これら一連の改修においては当然ながら AppSync API(スキーマ定義)も複数変更されており、上記エントリで言及した非同期処理用インターフェース/ラッパー用クエリの追加もあれば、同クエリ経由で非同期実行するように変更することで不要となったクエリ/ミューテーションの削除もありました。上記エントリで触れたテーブルの更新差分を導出する処理を例に挙げますと、一連の処理が AppSync クエリとして実装されていたところを 差分計算処理:非同期処理用インターフェース/ラッパー用クエリ経由の実行に変更 差分計算結果取得処理:S3 署名付き URL 経由での取得処理に変更 のように変更したため、元々の AppSync クエリは結果的に不要となり、Amplify のスキーマ定義からも削除しました。 一方で、差分計算結果のフォーマット(型情報)自体は変更する必要がなかったため、元々の AppSync クエリの返り値として Amplify のスキーマに定義していたものを引き続き使用しようと考え残しておきました。スキーマ定義の詳細については昨年度の以下エントリをご参照ください。 Amplify codegen で自動生成される query にネストされた type が 含まれない タイトルの通り、Amplify codegen で自動生成される query にネストされた type が 含まれない事象が発生したため、原因と解決策についてまとめてみました。 blog.usize-tech.com 2025.02.19 なお、この差分計算結果のフォーマットは、データ量の増大に伴いパフォーマンス改善の必要が生じたため、今年度改修しています。詳細な内容は改めて別のエントリで触れたいと思います。 ところが、一連の変更後にアプリケーションの動作確認をしたところ、差分計算結果取得処理においてアプリケーション側で以下のようなエラーが発生していました。文字通り、上記フォーマットの型情報が未定義で取得できなかったというエラーです。 name: TypeError message: can't access property "deleted_rows_info", models.DataDiffInfo is undefined おかしいな、Amplify のスキーマ定義にはそのまま残してあるのに・・と思いながら、Amplify が自動生成する型情報の定義ファイルとなる API.ts を参照したところ、 上記フォーマットに対応する型情報の定義が全て削除されていました。 そりゃ未定義と怒られる訳ですが、では何故削除されてしまったのかと言うと、、もうお分かりでしょうが Amplify のスキーマ定義から対象のクエリを削除したこと が原因でした。 というのも、この差分計算結果のフォーマット(型情報)は、対象のクエリの返り値の定義にしか含まれていなかったんですね。つまり、Amplify のスキーマ定義から対象のクエリを削除すると、Amplify・AppSync で定義される一連の処理(クエリ/ミューテーション/サブスクリプション)内でこのフォーマットが使用されなくなるため、Amplify(amplify codegen)により生成される型情報からも削除されるものと理解しました。Amplify スキーマ定義の目的が AppSync の graphql の仕様を定義するためと考えると、まあ当たり前と言えば当たり前の話ではあるのですが・・試しにスキーマ定義を元に戻したところ、API.ts 内の型情報定義も元に戻ったことも裏付けとなりました。 最も、AppSync のスキーマ定義自体には上記の型情報が含まれたままとなっていたので、若干一貫性のない仕様と感じてしまった部分もあります。このへんも Amplify gen2 ではそもそものスキーマ定義の方法が異なる以上、同様に変わっているところかもしれませんが・・ 回避策 先述の通り、Amplify が生成する型情報に含めるためには廃止したクエリを(使わないのに)再度定義する必要があるということで、さすがにそれは目的と手段を履き違えているため、大人しくこれらの型情報を定義するための composable を作成することで対応しました。 Amplify 経由で定義する方が正直楽ではあるので最初はその方向性で調査していたのですが情報がまるでなく。。一旦上記で解決したこともあり直近では特に調査していないのですが、もし今後続報があれば本エントリを更新したいと思います。 まとめ 先述の通り、Amplify のスキーマの目的というか位置づけを考えるとそのような挙動になることは納得の行くところですが、そもそも普通は Amplify/AppSync で使用しない型情報を明示的に Amplify のスキーマに定義することはないと思うので、そう考えるとある意味盲点と言えるかもしれません。。アプリケーション開発中に AppSync クエリの改廃自体は発生し得ることだとは思いますが、今回のように AppSync のクエリとしては不要になったが型情報は継続して使用したいというのもレアケース寄りだと思いますし・・ 本記事がどなたかの役に立てば幸いです。
こんにちは、SCSKで テクニカルエスコートサービス を担当してる兒玉です。 前回途中だった Spamhaus に登録されてしまったら通知する仕組みの動作確認と修正を行います。 動作確認 通常は Amazon EventBridge から n 時間(設定値)間隔で Lambda 関数を実行するのですが、今回はテストなので手っ取り早く Lambda関数のテストから実行します。 はい、失敗しました。 ログを確認すると、lambda_function というモジュールが無い、というエラーですね。うまくLambda関数がデプロイ出来ていないように思えます。 Lambda関数を確認すると、index.py しかありません。 CloudFormation テンプレートに戻って確認すると、インラインで Pythonのソースコードのテキストを zip ファイル化してアップロードする記述になっています。これでは動きません。 方針転換 なぜこの様な失敗になったのかというと、CloudFormationの ZipFile プロパティは、外部ライブラリを含めることができず、コードサイズにも制限があります。 AWS::Lambda::Function Code - AWS CloudFormation Changes to a deployment package in Amazon S3 or a container image in ECR are not detected automatically during stack upd... docs.aws.amazon.com また、CloudFormation 上では Lambda関数のハンドラの含まれるモジュール名を lambda_function と定義しているのですが、 index という名前になるよ、という注意書きもあります。 (出来上がった Lambda関数で index.py となっているのはこの為です) 今回は1ファイルに収まる対象のデプロイではなく、複数のモジュールやライブラリが必要だったため、AWS SAM(Serverless Application Model、サーバーレスアプリケーションを作成する際に最小限のコードで AWS Lambda 関数、Lambda の耐久性のある関数、Amazon API Gateway APIs、 Amazon DynamoDB テーブル、およびその他のサーバーレスリソースをすばやく定義できる仕組み)によるパッケージングが正解でした。 その他の案も含め、以下のような候補を考え、比較検討しました。 案1: S3バケットを別途用意して、deploy.sh 内でそこに zip化したソースコード群とライブラリを配置し、そこからデプロイするように CloudFormationテンプレートを修正する。 案2: AWS CDK へ移行する。 案3: AWS SAM を使って、CloudFormationテンプレートを修正する。SAMの機能を使ってソースコード群とライブラリをアップする。 案1は、S3バケットをこのために用意しなければならないので、今回はパス。案2のCDKはせっかく作った CloudFormationテンプレートを全部作り直しになって確認が大変そうなのでパス。案3の AWS SAM は S3 バケットは共通のものを用意してくれるし、zipファイル化もSAM側でやってくれるので余計な処理が要らなくなるし、テンプレートもスッキリする。他のリソースの場所はCloudFormationテンプレートのままでいけると思われるので、修正でいけそう、という考えで案3のAWS SAM にすることにしました。 AWS SAMについてはここでは詳細には紹介しませんが、詳しく知りたい方は以下をご参照ください。 AWS Serverless Application Model (AWS SAM) とは - AWS Serverless Application Model AWS Serverless Application Model (AWS SAM) とは何か、またデベロッパーがサーバーレスアプリケーションを構築するのに役立つ方法について説明します。 docs.aws.amazon.com Kiro再び Kiroに、要件変更の理由と、CloudFormationからSAMに変更する旨を伝えて、requirements、design、tasksの修正を依頼します。 全部が書き直されるのではなく、きちんと差分を理解していて、トレーサビリティマトリクスを元に再実行必要なタスクが洗いだされました。 またしてもタスク実行地獄です。 Task completeになったタスクを未実行に戻す方法がわからなかったので Kiro に聞いて戻してもらいました。 修正した箇所を見ると、単純にチェックボックス が [x] となっているのを [ ] にすれば良いようです。 Task 1を修正後 AWS SAM cli をインストールして、 sam validate-template 、 sam build が通ったので、Task 11、12、13 と再度実行します。 実行後、出来上がった SAMテンプレート を使用してデプロイすると、今度はlambda_function.py も ライブラリ群も含まれています。 1時間に1回の実行の設定でデプロイしたので、暫く経つと Lambda 関数にログが出力されているはずです。Lambda関数の モニタリングから CloudWatch Logs を確認してみると… おお、キチンとログが出力されていました。Kiro スペックモードでは、ログに関してもかなりバッチリ作ってくれたので、見やすいログが出力されています。 ちょっと興味があったので、Kiro に StructuredLogger の構成を Mermaid 図に起こしてもらったらこのようになっていました。 きちんと、センシティブな情報は SensitiveDataFilter でフィルタしてくれる処理も実装されているようです。 「プロトタイプからプロダクションまで(Agentic AI development from prototype to production)」と、いうキャッチフレーズに恥じない作りですね。 初期設定として 192.168.1.1 (プライベートIPアドレスなので、Spamhaus には登録されない)を監視対象としたので、 “is_listed” : false と、期待通りの実行結果です。 このアドレスを、試しに Spamhaus SBLに登録されてしまった検証に利用できる 127.0.0.2 を設定します。 「127.0.0.2」のテストIPについて 「Spamhausがテスト用に提供している 127.0.0.2 を入力することで、実際にブラックリストに登録された際と同じ挙動を安全にシミュレーションできます。」 Lambda関数の新しいバージョンを発行して、(ここではバージョン2) Lambda関数の live エイリアス値 を 1 -> 2 へ変更します。 live のリンクからになったところで、テストを実行すれば、Spamhausの SBL に登録されてしまったメールが来るはずです。 SNSからメールが来ました! うまく作成できたようです! 出来上がりイメージ 出来上がったコードはかなりしっかりと作ってくれたこともあり、すべてを この blog 上で乗せることができないような分量になってしました。 ですが、せっかくですので、アーキテクチャ図と、出来あがった SAM テンプレートを乗せておきます。 アーキテクチャ図は Kiro に書いてもらったものが全部入りで混沌としていたので、適度な範囲で分割しました。Mermaid記法 はテキストベースなので、分割したり修正したりが簡単なのが便利ですね! SAM の デプロイアーキテクチャ図 SBL Monitor のメインアーキテクチャ図 Lambda関数のアーキテクチャ SAM テンプレート AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: 'Spamhaus SBL Monitor - Serverless monitoring system for IP addresses on Spamhaus Blocklist using SAM with automatic dependency packaging' Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Monitoring Configuration" Parameters: - IPAddresses - MonitoringInterval - Label: default: "Notification Configuration" Parameters: - NotificationEmail - Label: default: "Resource Configuration" Parameters: - ResourceNamePrefix - CostAllocationTag ParameterLabels: IPAddresses: default: "IP Addresses to Monitor" MonitoringInterval: default: "Monitoring Interval (hours)" NotificationEmail: default: "Email Address for Notifications" ResourceNamePrefix: default: "Resource Name Prefix" CostAllocationTag: default: "Cost Allocation Tag Name" Parameters: IPAddresses: Type: String Description: 'Comma-separated list of IP addresses to monitor for SBL registration' AllowedPattern: '^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(,\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})*$' ConstraintDescription: 'Must be valid IPv4 addresses separated by commas (e.g., 192.168.1.1,10.0.0.1)' Default: '192.168.1.1' MonitoringInterval: Type: Number Description: 'Interval in hours between SBL checks (1-24 hours)' MinValue: 1 MaxValue: 24 Default: 1 ConstraintDescription: 'Must be between 1 and 24 hours' NotificationEmail: Type: String Description: 'Email address to receive SBL detection notifications' AllowedPattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' ConstraintDescription: 'Must be a valid email address' ResourceNamePrefix: Type: String Description: 'Prefix for all resource names (maximum 10 characters)' MaxLength: 10 MinLength: 1 AllowedPattern: '^[a-zA-Z][a-zA-Z0-9-]*$' ConstraintDescription: 'Must start with a letter and contain only alphanumeric characters and hyphens' Default: 'sbl' CostAllocationTag: Type: String Description: 'Value for the Cost allocation tag applied to all resources' MinLength: 1 MaxLength: 256 AllowedPattern: '^([\p{L}\p{Z}\p{N}_.:\/=+\-@]*)$' ConstraintDescription: 'Must contain only alphanumeric characters, spaces, periods, colons, slashes, equals, plus, hyphens, underscores, and at symbols' Default: 'spamhaus-monitor' Globals: Function: Runtime: python3.13 MemorySize: 256 Timeout: 300 Environment: Variables: LOG_LEVEL: INFO Tags: Cost: !Ref CostAllocationTag Application: 'Spamhaus-SBL-Monitor' Tracing: Active ReservedConcurrentExecutions: 5 AutoPublishAlias: live DeploymentPreference: Type: AllAtOnce # Stack-level tags applied to all resources (moved to Metadata section for SAM compatibility) Conditions: IsSingleHour: !Equals [!Ref MonitoringInterval, 1] Resources: # IAM Role for Lambda Function (Least Privilege) SBLMonitorFunctionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub '${ResourceNamePrefix}-lambda-execution-role' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess Policies: - PolicyName: SBLMonitorPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - secretsmanager:GetSecretValue Resource: !Ref DQSSecret - Effect: Allow Action: - sns:Publish Resource: !Ref SBLNotificationTopic - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${ResourceNamePrefix}-sbl-monitor:*' Tags: - Key: Cost Value: !Ref CostAllocationTag - Key: Application Value: 'Spamhaus-SBL-Monitor' - Key: Component Value: 'IAM-Role' # Lambda Function for SBL Monitoring SBLMonitorFunction: Type: AWS::Serverless::Function Properties: FunctionName: !Sub '${ResourceNamePrefix}-sbl-monitor' CodeUri: src/ Handler: lambda_function.lambda_handler Runtime: python3.13 Role: !GetAtt SBLMonitorFunctionRole.Arn Description: 'Monitors IP addresses for Spamhaus SBL registration and sends notifications with automatic dependency packaging' Environment: Variables: IP_ADDRESSES: !Ref IPAddresses DQS_SECRET_NAME: !Ref DQSSecret SNS_TOPIC_ARN: !Ref SBLNotificationTopic Events: ScheduledMonitoring: Type: Schedule Properties: Schedule: !If - IsSingleHour - 'rate(1 hour)' - !Sub 'rate(${MonitoringInterval} hours)' Name: !Sub '${ResourceNamePrefix}-sbl-monitor-schedule' Description: 'Scheduled trigger for Spamhaus SBL monitoring with retry and error handling' Enabled: true RetryPolicy: MaximumRetryAttempts: 3 DeadLetterConfig: Arn: !GetAtt SBLMonitorDLQ.Arn Tags: Component: 'Lambda-Function' # Dead Letter Queue for failed EventBridge executions SBLMonitorDLQ: Type: AWS::SQS::Queue DeletionPolicy: Delete UpdateReplacePolicy: Delete Properties: QueueName: !Sub '${ResourceNamePrefix}-sbl-monitor-dlq' MessageRetentionPeriod: 1209600 # 14 days Tags: - Key: Cost Value: !Ref CostAllocationTag - Key: Application Value: 'Spamhaus-SBL-Monitor' - Key: Component Value: 'SQS-DLQ' # SNS Topic for SBL Notifications SBLNotificationTopic: Type: AWS::SNS::Topic Properties: TopicName: !Sub '${ResourceNamePrefix}-sbl-notifications' DisplayName: 'Spamhaus SBL Detection Alerts' KmsMasterKeyId: alias/aws/sns DeliveryStatusLogging: - Protocol: http/s SuccessFeedbackRoleArn: !GetAtt SNSLoggingRole.Arn FailureFeedbackRoleArn: !GetAtt SNSLoggingRole.Arn Tags: - Key: Cost Value: !Ref CostAllocationTag - Key: Application Value: 'Spamhaus-SBL-Monitor' - Key: Component Value: 'SNS-Topic' # IAM Role for SNS Delivery Status Logging SNSLoggingRole: Type: AWS::IAM::Role Properties: RoleName: !Sub '${ResourceNamePrefix}-sns-logging-role' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: sns.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: SNSLogsDeliveryRolePolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents - logs:PutMetricFilter - logs:PutRetentionPolicy Resource: '*' Tags: - Key: Cost Value: !Ref CostAllocationTag - Key: Application Value: 'Spamhaus-SBL-Monitor' - Key: Component Value: 'IAM-Role' # Email Subscription to SNS Topic SBLNotificationSubscription: Type: AWS::SNS::Subscription Properties: TopicArn: !Ref SBLNotificationTopic Protocol: email Endpoint: !Ref NotificationEmail FilterPolicy: severity: - CRITICAL - WARNING FilterPolicyScope: MessageAttributes DeliveryPolicy: healthyRetryPolicy: numRetries: 3 minDelayTarget: 20 maxDelayTarget: 20 numMinDelayRetries: 0 numMaxDelayRetries: 0 numNoDelayRetries: 0 backoffFunction: linear # Secrets Manager Secret for DQS Key DQSSecret: Type: AWS::SecretsManager::Secret DeletionPolicy: Delete UpdateReplacePolicy: Delete Properties: Name: !Sub '${ResourceNamePrefix}-dqs-key' Description: 'Spamhaus Data Query Service (DQS) authentication key with encryption and access policies' SecretString: | { "DQS_KEY": "CHANGE-YOUR-DQS-KEY-LATER" } KmsKeyId: alias/aws/secretsmanager ReplicaRegions: [] Tags: - Key: Cost Value: !Ref CostAllocationTag - Key: Application Value: 'Spamhaus-SBL-Monitor' - Key: Component Value: 'Secrets-Manager' # Resource Policy for DQS Secret (Least Privilege Access) DQSSecretResourcePolicy: Type: AWS::SecretsManager::ResourcePolicy Properties: SecretId: !Ref DQSSecret ResourcePolicy: Version: '2012-10-17' Statement: - Sid: AllowLambdaAccess Effect: Allow Principal: AWS: !GetAtt SBLMonitorFunctionRole.Arn Action: - secretsmanager:GetSecretValue Resource: '*' Condition: StringEquals: 'secretsmanager:ResourceTag/Application': 'Spamhaus-SBL-Monitor' Outputs: LambdaFunctionArn: Description: 'ARN of the SBL Monitor Lambda function' Value: !GetAtt SBLMonitorFunction.Arn Export: Name: !Sub '${AWS::StackName}-LambdaFunctionArn' SNSTopicArn: Description: 'ARN of the SBL Notification SNS topic' Value: !Ref SBLNotificationTopic Export: Name: !Sub '${AWS::StackName}-SNSTopicArn' SecretsManagerArn: Description: 'ARN of the DQS Key secret in Secrets Manager' Value: !Ref DQSSecret Export: Name: !Sub '${AWS::StackName}-SecretsManagerArn' DeadLetterQueueUrl: Description: 'URL of the Dead Letter Queue for failed executions' Value: !Ref SBLMonitorDLQ Export: Name: !Sub '${AWS::StackName}-DeadLetterQueueUrl' MonitoringConfiguration: Description: 'Summary of monitoring configuration' Value: !Sub 'Monitoring ${IPAddresses} every ${MonitoringInterval} hours, notifications to ${NotificationEmail}' おわりに Spamhaus に登録されてしまったら通知する仕組みをサーバレスで作成しました。 Kiro が作成してくれたコードは、分量の関係で全て公開はできませんでしたが、しっかりとした構成で処理を記載してくれていましたし、アーキテクチャ図やログ出力の丁寧な作りは納得の出来でした! みなさんも良い ダイレクトメール管理生活を!
こんにちは、SCSKで テクニカルエスコートサービス を担当している兒玉です。 前回途中だった Spamhaus に登録されてしまったら通知する仕組みの実装を進めます。 本記事(中編)では、実装完了までを解説します。 タスクの確認 前回、設計ドキュメントとして作成したアーキテクチャ図です。こんな感じで考えていました。 Kiro が作成してくれたタスクは大項目 14 項目、小項目も合わせると何と 56 項目あります。”*”のついている項目は optional で 21項目あるのでそれを引いても 35 項目もあるわけなので、テキパキと進めてゆきます。 - [ ] 1. Set up project structure and core interfaces - [ ] 2. Implement DNS query and SBL checking functionality - [ ] 2.1 Implement DNS query module with nslookup functionality - [ ]* 2.2 Write property test for DNS query format compliance - [ ] 2.3 Implement SBL response interpretation - [ ]* 2.4 Write property test for DNS response interpretation - [ ] 3. Implement AWS Secrets Manager integration - [ ] 3.1 Create DQS key retrieval module - [ ]* 3.2 Write property test for secure DQS key retrieval - [ ]* 3.3 Write unit tests for Secrets Manager error handling - [ ] 4. Implement IP address processing and monitoring logic - [ ] 4.1 Create IP configuration parser - [ ]* 4.2 Write property test for IP configuration storage - [ ] 4.3 Implement sequential IP processing with timing - [ ]* 4.4 Write property test for sequential processing with timing - [ ]* 4.5 Write property test for complete IP monitoring - [ ] 5. Checkpoint - Core functionality validation - [ ] 6. Implement SNS notification system - [ ] 6.1 Create notification message formatter - [ ]* 6.2 Write property test for comprehensive notification content - [ ] 6.3 Implement SNS publishing with error handling - [ ]* 6.4 Write property test for SBL detection notification - [ ]* 6.5 Write property test for SNS failure resilience - [ ] 7. Implement duplicate detection prevention - [ ] 7.1 Create SBL detection state management - [ ]* 7.2 Write property test for duplicate detection prevention - [ ]* 7.3 Write property test for notification consolidation - [ ] 8. Implement comprehensive error handling and logging - [ ] 8.1 Create structured logging system - [ ]* 8.2 Write property test for structured logging with context - [ ] 8.3 Implement rate limiting and retry logic - [ ]* 8.4 Write property test for rate limiting handling - [ ]* 8.5 Write property test for sensitive information protection - [ ] 9. Create main Lambda handler - [ ] 9.1 Implement main lambda_handler function - [ ]* 9.2 Write property test for SBL detection recording - [ ]* 9.3 Write integration tests for complete workflow - [ ] 10. Checkpoint - Lambda function validation - [ ] 11. Create CloudFormation template - [ ] 11.1 Create base CloudFormation template structure - [ ] 11.2 Define Lambda function resources - [ ] 11.3 Define EventBridge scheduler resources - [ ] 11.4 Define SNS topic and subscription resources - [ ] 11.5 Define Secrets Manager resources - [ ] 11.6 Define IAM roles and policies - [ ] 11.7 Implement resource tagging and naming - [ ] 12. Create deployment scripts and documentation - [ ] 12.1 Create CloudShell deployment script - [ ]* 12.2 Write deployment validation tests - [ ] 12.3 Create deployment documentation - [ ] 13. Final integration and validation - [ ] 13.1 Deploy and test complete system - [ ]* 13.2 Write property tests for configurable scheduling - [ ]* 13.3 Write property tests for execution failure resilience - [ ]* 13.4 Write remaining property tests - [ ] 14. Final checkpoint - Complete system validation Keep optional tasks(faster MVP) を使ってとにかく一通り作ってしまうことにします。 Kiroの左上の Explorer アイコン → .kiro/specs/<プロジェクト名>/task.md をクリックして tasks.md を開きます。 タスクリストが表示されます。 既に完了したタスクは チェックマークと、Task completed表示、Optionalのタスクは グレー表示、まだ実行していないTaskは 白表示されています。 各タスクの左上の ⚡️ Start task をクリックするとタスクが開始します。 タスクを進めていると、Kiro からコマンド実行の許可を求められるので、問題のないコマンドかどうか確認しながら許可を与えてゆきます。一つ一つのタスクは小さくとも、数が多いとなかなか終わりません。疲れをしらない生成AIに対して実行の様子を見ながら許可を与えたり何度も同じ処理を行うような状態で詰まってしまった場合にアドバイスをしたりするこちらのほうがへばってきてしまうくらいです。 Kiro と格闘すること 6 時間、ようやくコアタスクがすべて終了しました。タスクを進める中で気づいたコツは、 Kiroの提案を鵜呑みにしすぎないこと です。 詰まってしまった場合には、以下のような具体的な指示を出すのが早期解決の近道でした: 「状況が詳細にわかるようにデバッグ文を追加してください」 「このエラーの原因を掴めそうな調査処理を追加してください」 まさに、AIを「部下」として適切にリードする感覚ですね。 README.mdのアップデート タスクによる作業中に、詳細なコマンドの実行パラメータ等が定義されたり、不具合で修正されたり、途中仕様変更でパラメータ追加が必要となったりで、当初に作成したドキュメントとは違った形になっていることもありますので、 README.md を現在の形にアップデートするように Kiro にお願いします。 これで、環境にデプロイする準備が整いました。 デプロイ コードは書き上がって、単体、結合テストも成功したものの、実際に動作するかは別です。 これだけ丁寧に検討してタスクを分けても実環境で動作を確認しているわけではないのでどうしても不具合は発生してしまいます。 更新された README.md ファイルを元にデプロイします。 環境変数または引数にパラメータを入れて、 ./deploy.sh を実行 成功しました! 実行時のログを見ると、(スクリーンショットでは途中切れていますが)必要なリソースはデプロイされたように見えます。 スクリプトの指示に従って、Secrets Manager の キーと、SNSのSubsctiptionの承認を行います。 Secrets Manager の Secret 値を Spamhaus から取得したキーに置き換える メールを確認して Subscriptionを承認する サブスクリプションのステータスが “確認済み” になればOK これで準備完了です。 今回はここまで。 次回は、動作確認と修正を行います。
こんにちは、SCSKで テクニカルエスコートサービス を担当している兒玉です。 自社のサービスの情報などを メールで配信されている方はかなり多いのではないでしょうか。 そんな メール 配信をお仕事の一部とされている人の悩みの一つが、自社の配信システムがSPAM配信者としてマークされてしまって配信不能になってしまうことです。 もちろんそこに登録されないように不達メールを投げないようにメーリングリストの対象をメンテナンスしたり、DKIM、DMARCなどの仕組みを導入したりはもちろん行っていると思いますが、その上でもどうしても登録されてしまった場合に気が付けないと何のアクションも取ることができません。毎日人手をつかって確認するのも大変なので、AWSで自動化してみようということにしました。 本記事(前編)では、生成AIを活用して要件定義から設計、実装計画を立てるまでを解説します。 事前調査 自分のIPアドレスが、Spamhaus の SBL (Spamhaus Blocklist) に登録されているかどうかは誰でも確認できます。 Attention Required! | Cloudflare check.spamhaus.org ※商用利用には、Fair Use Policyに基づくライセンスが必要です。 Spamhaus DNS Blocklists Fair Use Policy www.spamhaus.org 調査を進めて行って、わかったことをファイルに雑多にまとめていきます。 Kiro に要求を伝える まとめたファイルと、それを整理してやってほしいこと、使ってもらいたい言語や環境を整理します。 # Spamhaus に登録されている場合に通知をする仕組みをAWSサーバレスで構築 ## 目的 ダイレクトメール送信サービスを運営しています。 Spamhausというスパムメールが送信されてくる IP アドレスなどのリストを管理している団体があるのですが、その団体が管理しているSBL (Spamhaus Blocklist) に自分のIPアドレスが登録されてしまった場合には、email に通知が来るようにしたいです。 ## 仕様要件 調査は簡単にかつ安価に行いたいので、EventBridge で定期的(1時間に一回, パラメータで変更可能)に自分の使用している IP アドレス(複数)を Lambda 関数を使用して nslookup コマンドを使用して SBL (Spamhaus Blocklist) からの応答を確認して、登録されている場合には Amazon SNS 経由で email で管理者に通知するシステムを構築します。 - Lambda関数の言語は Python 3.13 とします。 - CloudFormationを使用してデプロイします。 - CloudShell からデプロイします。 - 作成されるリソースのうち、タグ付け可能なリソースにはコスト配分タグとして Cost: <指定されたコスト配分タグ名> を付けます。 - 問い合わせに必要な Spamhaus から払い出されるDQSキー(Data Query Service Key 、サービスへのクエリが誰からのものか特定するキーのようなもの、文字列)は Secrets Manager から取得する - CloudFormation テンプレートでは、Secrets ManagerのString値としては "CHANGE-YOUR-DQS-KEY-LATER" として作成し、デプロイ後別途変更することとする。 - パラメータとして以下の値を指定します - IP アドレス(単数あるいは複数) - 調査間隔 n 時間間隔(時間単位、デフォルトは1時間間隔) - リソースの タグ名 Cost につける コスト配分タグ名> - リソースのNameタグの Prefix として付与される prefix (10文字以内-長いと制約に引っかかってエラーになるリソースが出るため) DNS問い合わせの仕組みなので大丈夫だと思いますが、連続して問い合わせを行うとこちらがスパマー認定されてしまっては困るので、複数IPアドレスに対する問い合わせの場合には 1秒 待ちを入れることにします ## DQS Keyの形式 abcdefghijklmnopq123456789 のような形式です。 ## DQS Keyを使用した問い合わせ nslookup [逆順IP].[DQS_KEY].[リスト名].dq.spamhaus.net ですが、今回は SBL(Spamhaus Blocklist)が対象なので、 ```bash nslookup [逆順IP].[DQS_KEY].sbl.dq.spamhaus.net ``` となります。 ### サンプル応答 #### OK応答例(SPAM IPと判定されていない) ```bash $ nslookup 109.62.249.54.[DQS_KEY].sbl.dq.spamhaus.net Server: 127.0.0.53 Address: 127.0.0.53#53 ** server can't find 109.62.249.54.[DQS_KEY].sbl.dq.spamhaus.net: NXDOMAIN ``` #### NG応答例(SPAM IPと判定されている) ```bash $ nslookup 2.0.0.127.[DQS_KEY].sbl.dq.spamhaus.net Server: 127.0.0.53 Address: 127.0.0.53#53 Non-authoritative answer: Name: 2.0.0.127.[DQS_KEY].sbl.dq.spamhaus.net Address: 127.0.0.2 ``` ## ブロックのリターンコード 各ブロックリストのリターンコード SpamhausのDNSBLでは、異なるリストに対して異なるリターンコードが使用されます: リスト リターンコード 意味 SBL 127.0.0.2 スパム送信源 CSS 127.0.0.3 低評価送信者 XBL 127.0.0.4-127.0.0.7 乗っ取られたホスト PBL (ISP提供) 127.0.0.10 ISPが提供したポリシーリスト PBL (Spamhaus) 127.0.0.11 Spamhausが推定したポリシーリスト DROP 127.0.0.9 悪意のある組織(SBLと併用) # 参考資料 spamhaus_pre_survey.ini に、事前調査した結果を配置しました。 このテキストを、Kiro に伝えます。Spec Mode を選択するのを忘れずに。 しばらく考えて、要件ドキュメント(requirements.md)を作成してくれます。用語集も作成してくれるのが初めてこのドキュメントを見ることになる人にとっては嬉しいのと、日本のシステムの仕様書などにはあまり書かれない ユーザーストーリー が書かれているのが嬉しいですね。これは誰がどうしたいためのシステムだとわかりやすく、また、後日見たときに ”この機能は何のために入ってるんだっけ?” という判断をする際にも有用です。 英語で作成してくれますが、LLMなので、日本語に翻訳してもらうこともできます。せっかくなので別ファイルで翻訳もしてもらいました。 英語と日本語の requirements を見比べていて気が付きました。英語の方は WHEN …, The System SHALL … という形式になっていて、やること、誰が、どんなときにするのかわかりやすい気がします。日本語でもその訳文なので、…のときシステムは…しなければならない となるのですが、記法を縛らない限り日本語では主語がなくても違和感がないので、勘違いしないように読むのはなかなか大変なこともありますが、記法がはっきりしている英語版のほうがわかりやすいかもしれません。あと、 “SHALL” “WHEN” “THE System” が大文字になっていて見やすいんです。 無理やり日本語でも使おうとすれば、 WHEN レートリミット超過が発生したら, システム SHALL exponential backoff ロジックでリトライする。 とかにするか、”のとき” “システムは” “しなければならない”を太字にするとかでしょうか。日本語だと少し不自然な表現になることもありますが、構造を理解するには英語版の方が適していると感じました。 要件ドキュメントをレビューして、OKなら、Move to Design phase ボタンで次に進みます。 設計ドキュメントの作成 要件ドキュメントから、Kiro が 設計ドキュメント(Design.md)を作成してくれます。 設計ドキュメントでは、テキストベースで図を記載する事のできる Mermaid 記法という記法でアーキテクチャ図を記述してくれます。 Kiro に Markdown Mermaid 図の preview 機能拡張を追加して preview することで図として表示してみることができます。 テキストだけの説明だとこの図を頭に浮かべるのは難しいので、Mermaid図化してくれるのありがたいですね! これを見るだけでわかった気になれます。 設計ドキュメント(design.md) を見て足りないと思ったところがあれば、追加を指示します。問題なければ Move to implementation plan ボタンで次の工程に進みます。 設計ドキュメントを元に、実装計画(tasks.md) を作成してくれました。 ここでは、 コア部分のみのタスクを実装するか、全タスクを順に実装していくのかを選択できます。 全タスクの実行にはそれなりに時間がかかるので、さっと作りたいときはコア部分のみ(Keep optional tasks(faster MVP))を選択します。 後から全部実行したり個別に実行したりもできるので、この時点でどちらか決まってしまうというわけではありませんので安心してください。 今回はここまで。
こんにちは、広野です。 これまでいろいろと React アプリを AWS リソースと連携させるための仕組みをブログ記事にしてきましたが、絶対に使うであろうユースケースを書いていませんでした。以下、順を追って説明したいと思います。 本記事は UI 編です。 前回の記事 こちらをご覧になった上で、本記事にお戻りください。 アーキテクチャ編では、背景やアーキテクチャを説明しています。 React アプリで Amazon Cognito 認証済みユーザーにのみ Amazon S3 静的コンテンツへのアクセスを許可したい -アーキテクチャ編- Amazon Cognito でユーザー認証する React アプリで、Amazon CloudFront 経由の Amazon S3 へのアクセス制御を実装する方法を紹介します。本記事はアーキテクチャ編です。 blog.usize-tech.com 2026.01.13 環境編では、主に Lambda@Edge 関数や CloudFront による環境づくりの説明をしています。 React アプリで Amazon Cognito 認証済みユーザーにのみ Amazon S3 静的コンテンツへのアクセスを許可したい -環境編- Amazon Cognito でユーザー認証する React アプリで、Amazon CloudFront 経由の Amazon S3 へのアクセス制御を実装する方法を紹介します。本記事は環境編です。 blog.usize-tech.com 2026.01.13 React アプリ側の実装 以下の Amazon CloudFront にリクエストを投げる React アプリ側のコードを説明します。 リクエストにはパターンがある リクエストにはいろいろなパターンがあります。 画像 の取得。<img>タグが例。 PDF などのファイルへの リンク 。<a>タグが例。 動画 のストリーミング。 HTTP GET メソッド によるデータの取得。 当然それぞれのコードは異なるのと、いずれもデフォルトでは Authorization ヘッダー埋め込みに対応していないので、ひとつひとつに対してコードをカスタマイズする必要があります。 しかし、とにかくやることは リクエストの Authorization ヘッダーにトークンを埋め込むこと です。それさえできればよいのです。 リクエストパターンごとのコード紹介 ということで、リクエストのパターンごとに実際のコード例を紹介します。 共通の設計にしたことが、最新のトークン取得です。AWS Amplify のモジュールを使用して、Amazon Cognito に問い合わせします。古いトークンを使用してしまうことを避けるためリクエスト直前に都度トークンを取得する設計にしています。 画像 <img> タグをカスタマイズします。<AuthImg> というカスタムコンポーネントを作成し、<img> の代わりに使用します。 画像のバイナリデータを取得し、仮の URL が作られ、それを <img> に埋め込む動きをしています。 カスタムコンポーネント import { useState, useEffect } from 'react'; import { fetchAuthSession } from 'aws-amplify/auth'; //Cognito認証付きS3へのカスタムimgコンポーネント const AuthImg = ({ src, alt, className, ...props }) => { const [imgSrc, setImgSrc] = useState(""); useEffect(() => { let objectUrl = ""; const fetchImage = async () => { try { const { tokens } = await fetchAuthSession(); const idToken = tokens.idToken.toString(); const response = await fetch(src, { method: 'GET', headers: { 'Authorization': `Bearer ${idToken}` } }); if (!response.ok) throw new Error('Image fetch failed'); const blob = await response.blob(); objectUrl = URL.createObjectURL(blob); setImgSrc(objectUrl); } catch (error) { console.error("Failed to load authenticated image:", error); } }; if (src) { fetchImage(); } // クリーンアップ関数:コンポーネントが消える時にメモリを解放 return () => { if (objectUrl) URL.revokeObjectURL(objectUrl); }; }, [src]); // 読み込み中は何も出さない、またはプレースホルダーを出す if (!imgSrc) return <div className={className} style={{ backgroundColor: '#eee' }} />; return <img src={imgSrc} alt={alt} className={className} {...props} />; }; 使用時のコード <AuthImg className="figureimg" src={"CloudFrontのURL/指定のパス/xxxxx.png"} alt="xxxxx" /> リンク <a>タグをカスタマイズする、はずなんですが、私は MUI を使用しているので、 MUI の Link コンポーネント をカスタマイズしました。<AuthLink> というカスタムコンポーネントを作成し、<Link> の代わりに使用します。たぶん a タグも同様にすれば動くと思います。 リンク先ファイルのバイナリデータを取得し、仮の URL が作られ、それを <Link> に埋め込む動きをしています。別タブで表示する例です。 カスタムコンポーネント import { fetchAuthSession } from 'aws-amplify/auth'; import { Link } from '@mui/material'; //Cognito認証付きS3へのカスタムMuiLinkコンポーネント const AuthLink = ({ href, children, ...props }) => { const handleAuthClick = async (event) => { //通常のリンク遷移(認証なしアクセス)を防止 event.preventDefault(); try { //トークン取得 const { tokens } = await fetchAuthSession(); const idToken = tokens.idToken.toString(); //認証ヘッダー付きでリクエスト const res = await fetch(href, { method: 'GET', headers: { 'Authorization': `Bearer ${idToken}` } }); if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`); //バイナリデータを取得して表示用のURLを生成 const blob = await res.blob(); const objectUrl = URL.createObjectURL(blob); //別タブで開く window.open(objectUrl, '_blank'); } catch (error) { console.error("Authenticated link access failed:", error); alert("ファイルの取得に失敗しました。"); } }; return ( <Link {...props} href={href} onClick={handleAuthClick} sx={{ cursor: 'pointer', ...props.sx }}> {children} </Link> ); }; 使用時のコード <AuthLink href={"CloudFrontのURL/指定のパス/xxxxx.pdf"} target="_blank" rel="noopener"> 表示するテキスト </AuthLink> GET メソッド Amazon S3 に置いた JSON データのテキストファイルを取得するのに使用しました。axios をカスタマイズして getAuthAxios という関数にしました。md ファイルのテキストデータ取得にも使えます。 カスタム関数 import { fetchAuthSession } from 'aws-amplify/auth'; import axios from 'axios'; //Cognito認証付きS3へのget関数 const getAuthAxios = async () => { const { tokens } = await fetchAuthSession(); const idToken = tokens.idToken.toString(); return axios.create({ headers: { Authorization: `Bearer ${idToken}` } }); }; 使用時のコード const [data, setData] = useState(); const getData = async () => { const api = await getAuthAxios(); const res = await api.get("CloudFrontのURL/指定のパス/xxxxx.json", { cache: "no-store" }); setData(res.data); }; ストリーミング動画 (HLS) HLS 形式のストリーミング動画を認証付きで取得するのに、react-player の config オプションを適切に設定しました。v3.4.0 だと以下のコードで動きます。ネット上の情報だと古いバージョンの情報が多かったので、気を付けてください。 使用時のコード import { fetchAuthSession } from 'aws-amplify/auth'; import ReactPlayer from 'react-player'; <div className="player-wrapper"> <ReactPlayer src="CloudFrontのURL/指定のパス/xxxxx.m3u8" className="react-player" controls={true} width="100%" height="100%" playing={false} config={{ hls: { xhrSetup: async function(xhr, url) { const { tokens } = await fetchAuthSession(); const idToken = tokens.idToken.toString(); xhr.setRequestHeader('Authorization', `Bearer ${idToken}`); } } }} /> </div> react-player の詳細な設定情報は公式ドキュメントをご覧ください。 GitHub - cookpete/react-player: A React component for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable, Vimeo, Wistia and DailyMotion A React component for playing a variety of URLs, including file paths, YouTube, Facebook, Twitch, SoundCloud, Streamable... github.com まとめ いかがでしたでしょうか? いちいちリクエストの Authorization ヘッダーにトークンを埋め込まないといけないので苦労しましたが、一度カスタムコンポーネントを作ってしまえば使い回しできるので楽ですね。 本記事が皆様のお役に立てれば幸いです。
こんにちは、広野です。 これまでいろいろと React アプリを AWS リソースと連携させるための仕組みをブログ記事にしてきましたが、絶対に使うであろうユースケースを書いていませんでした。以下、順を追って説明したいと思います。 本記事は環境編です。今後、 UI 編 を続編記事として用意しています。 前回の記事 前回記事で、背景やアーキテクチャを説明しています。こちらをご覧になった上で、本記事にお戻りください。 React アプリで Amazon Cognito 認証済みユーザーにのみ Amazon S3 静的コンテンツへのアクセスを許可したい -アーキテクチャ編- Amazon Cognito でユーザー認証する React アプリで、Amazon CloudFront 経由の Amazon S3 へのアクセス制御を実装する方法を紹介します。本記事はアーキテクチャ編です。 blog.usize-tech.com 2026.01.13 Lambda@Edge 関数の実装 以下の Lambda@Edge 関数について説明します。 Lambda@Edge 関数の制約 Lambda@Edge 関数を作成するときには、通常の Lambda 関数にはない制約があるので、注意が必要です。 Lambda@Edge に対する制限 - Amazon CloudFront Lambda@Edge に関する以下の制限を参照してください。 docs.aws.amazon.com 詳細は AWS 公式ドキュメントに掲載されていますが、私が気を遣ったところを挙げておきます。総じて、大量のリクエストやレスポンスに対して呼び出されるので、迅速かつ安全に実行できることを優先してつくられている、と感じました。 関数をバージニア北部リージョン (us-east-1) にデプロイしなければならない。これは CloudFront と同じなので妥当ですよね。 ただデプロイしただけでは CloudFront と関連付けられない。関数の「バージョン」を作成し、そのバージョンを CloudFront と関連付ける必要がある。 Lambda@Edge 関数に割り当てる IAM ロールは、通常の Lambda 関数で設定する lambda.amazonaws.com による引き受けだけでなく、edgelambda.amazonaws.com も含めた両方を設定する必要がある。権限は必要なものを付ければよいが、最低限 Amazon CloudWatch Logs にログを残す設定があれば OK。 環境変数は使用できない。そのため、環境変数はベタにコード内に埋め込む必要あり。AWS CloudFormation でデプロイする場合は !Sub でパラメータを埋め込めばよい。 Lambda レイヤーは使用できない。レイヤーが必要な高度な処理はできないということになる。モジュールを使えば一発の処理でも、ベタに JavaScript で書かないといけない局面がある。 AWS X-Ray は使用できない。 Amazon CloudWatch Logs にログが残らないことがある。これはデバッグで困りました。マネジメントコンソールで関数をテスト実行するときはログが残ったので、そこでデバッグしました。 さらに、私が実施したケースでは以下の件がありました。Lambda@Edge に限った話ではありませんが。 Lambda@Edge 関数のコードサンプルは Node.js が多く Python は少ない。そのため、ランタイムは最新の Node.js 24 を採用した。 Node.js の Lambda 関数を AWS CloudFormation でデプロイすると、デプロイされるコードのファイル名は index.js 固定になる。しかし Node.js 24 Lambda 関数が期待している拡張子は .mjs であり、その場合、コードを ES Modules (ESM) に従って書かないといけない。拡張子の変更は CloudFormation ではできないため、コードを古い CommonJS に従って書くことになる。 前述した Lambda 関数バージョンを AWS CloudFormation で発行することは可能。Lambda 関数そのものを変更するときに、同時に Lambda 関数バージョンの Description を更新してバージョンを更新する運用にした。 アーキテクチャ編にも掲載したが、CORS 対応のためレスポンスに Access-Control-Allow-Origin ヘッダーなどの CORS 関連ヘッダーを含める必要がある。環境変数が使用できないため、ベタにコードに書く必要あり。ただし、私の場合は CloudFront でオーバーライドする設計にしたため、Lambda@Edge 関数コード内に CORS 関連ヘッダーを書かなくて済んだ。 Lambda@Edge 関数コード 実際に動かしたコードはこちら。 上述の制約通り、Node.js 24 ですが CommonJS で書かれています。require(xxx)など。 正当なリクエストには、Authorization ヘッダーに “Bearer ” 付きの JWT が格納されている前提です。 'use strict'; const crypto = require('crypto'); const https = require('https'); // Cognito のアプリケーションクライアントID const CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxx"; // Cognito のユーザープールID とリージョン名を埋め込む const ISSUER = "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_xxxxxxxxx"; const JWKS_URL = `${ISSUER}/.well-known/jwks.json`; let cachedKeys = null; let cachedAt = 0; function fetchJson(url) { return new Promise((resolve, reject) => { https.get(url, res => { let data = ""; res.on("data", c => data += c); res.on("end", () => resolve(JSON.parse(data))); }).on("error", reject); }); } async function getJwks() { if (cachedKeys && Date.now() - cachedAt < 3600000) return cachedKeys; cachedKeys = await fetchJson(JWKS_URL); cachedAt = Date.now(); return cachedKeys; } function b64u(str) { str += "=".repeat((4 - str.length % 4) % 4); return Buffer.from(str.replace(/-/g, "+").replace(/_/g, "/"), "base64"); } exports.handler = async (event) => { const req = event.Records[0].cf.request; // 1. CORS プリフライトチェックであればすぐにレスポンスを戻す。 // status コードを入れることでユーザーに戻る。 /* ========== OPTIONS preflight ========== */ if (req.method === "OPTIONS") { return { status: "204", statusDescription: "No Content" }; } // 2. 認証不要なリクエストであればそのまま S3 に流す。 // ここではパスが /public/ から始まるリクエストであれば認証なしで通すようにしている。 // return req と書くことで S3 に流れる。 /* ========== Public images bypass ========== */ if (req.uri.startsWith("/public/")) { return req; } /* ========== Authorization required ========== */ // 3. ここからはトークンのチェックをしている。正当なトークンでなければ fail のレスポンスを返す。 const fail = { status: "403", statusDescription: "Forbidden" }; const auth = req.headers.authorization?.[0]?.value; if (!auth) return fail; try { const token = auth.replace("Bearer ", ""); const parts = token.split("."); if (parts.length !== 3) return fail; const [h, p, s] = parts; const header = JSON.parse(b64u(h).toString()); const payload = JSON.parse(b64u(p).toString()); if (payload.iss !== ISSUER || payload.aud !== CLIENT_ID || payload.exp < Date.now() / 1000) { return fail; } const jwks = await getJwks(); const key = jwks.keys.find(k => k.kid === header.kid); if (!key) return fail; const pub = crypto.createPublicKey({ key, format: "jwk" }); const ok = crypto.verify("RSA-SHA256", Buffer.from(`${h}.${p}`), pub, b64u(s)); return ok ? req : fail; } catch (e) { return fail; } }; Amazon CloudFront 側の実装 セキュリティ設計上、以下の構成であることが前提です。説明は省略します。 オリジンは Amazon S3 バケットで、OAC が設定されている。(関連付けられた CloudFront でなければ S3 バケットにアクセスできない) Amazon Simple Storage Service オリジンへのアクセスを制限する - Amazon CloudFront Amazon CloudFront オリジンアクセスコントロール (OAC) で、Amazon S3 オリジンへのアクセスを制限します。 docs.aws.amazon.com ここまでで話した Lambda@Edge 関数ができていれば、Amazon CloudFront ディストリビューションと関連付けるのは簡単です。注意すべきは Lambda 関数のバージョンが発行できていないと関連付けできないことぐらいでしょうか。Amazon CloudFront なので IAM ロールを意識する必要はありません。 詳細な設定はこの後紹介する AWS CloudFormation テンプレートで紹介します。 AWS CloudFormation テンプレート 設定詳細は CloudFormation テンプレートで紹介します。適宜インラインで補足します。 Lambda@Edge 関数 (バージニア北部リージョン限定!!) AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates a Lambda@Edge function to authorize users access S3 contents. The resources must be deployed in us-east-1 region. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SubName: Type: String Description: System sub name. (e.g. prod or test) Default: test MaxLength: 10 MinLength: 1 // 重要。このパラメータを Lambda@Edge 関数変更の都度、書き換える必要がある。これにより関数のバージョンが更新される。 // さらに、新しいバージョンで Amazon CloudFront ディストリビューションも更新する必要あり。忘れないように。 Updated: Type: String Description: The updated date and time of the lambda function. You must update this value when you update the lambda function, and update the relevant CloudFront stack. Default: 2026-01-10T0900 MaxLength: 20 MinLength: 10 CognitoUserPoolId: Type: String Description: Cognito user pool ID. Default: ap-northeast-1_xxxxxxxxx MaxLength: 50 MinLength: 1 CognitoUserPoolRegion: Type: String Description: The region name where Cognito user pool is deployed. Default: ap-northeast-1 MaxLength: 30 MinLength: 1 CognitoUserPoolAppClientId: Type: String Description: Cognito user pool app client ID. Default: xxxxxxxxxxxxxxxxxxxxxxxxxx MaxLength: 50 MinLength: 1 Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "General Configuration" Parameters: - SubName - Updated - Label: default: "Cognito Configuration" Parameters: - CognitoUserPoolId - CognitoUserPoolAppClientId - CognitoUserPoolRegion Resources: # ------------------------------------------------------------# # Lambda@Edge Role (IAM) # ------------------------------------------------------------# LambdaAtEdgeRole: Type: AWS::IAM::Role Properties: RoleName: !Sub example-LambdaAtEdge-${SubName} Description: This role allows Lambda functions to push logs. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com - edgelambda.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole # ------------------------------------------------------------# # Lambda # ------------------------------------------------------------# LambdaAtEdge: Type: AWS::Lambda::Function Properties: FunctionName: !Sub example-s3auth-${SubName} Description: Lambda@Edge function to check authorization to access S3 contents Runtime: nodejs24.x Timeout: 3 MemorySize: 128 Role: !GetAtt LambdaAtEdgeRole.Arn Handler: index.handler Tags: - Key: Cost Value: !Sub example-${SubName} Code: // !Sub で環境変数をパラメータから埋め込んでいます。 ZipFile: !Sub | 'use strict'; const crypto = require('crypto'); const https = require('https'); const CLIENT_ID = "${CognitoUserPoolAppClientId}"; const ISSUER = "https://cognito-idp.${CognitoUserPoolRegion}.amazonaws.com/${CognitoUserPoolId}"; const JWKS_URL = `${!ISSUER}/.well-known/jwks.json`; let cachedKeys = null; let cachedAt = 0; function fetchJson(url) { return new Promise((resolve, reject) => { https.get(url, res => { let data = ""; res.on("data", c => data += c); res.on("end", () => resolve(JSON.parse(data))); }).on("error", reject); }); } async function getJwks() { if (cachedKeys && Date.now() - cachedAt < 3600000) return cachedKeys; cachedKeys = await fetchJson(JWKS_URL); cachedAt = Date.now(); return cachedKeys; } function b64u(str) { str += "=".repeat((4 - str.length % 4) % 4); return Buffer.from(str.replace(/-/g, "+").replace(/_/g, "/"), "base64"); } exports.handler = async (event) => { const req = event.Records[0].cf.request; /* ========== OPTIONS preflight ========== */ if (req.method === "OPTIONS") { return { status: "204", statusDescription: "No Content" }; } /* ========== Public images bypass ========== */ if (req.uri.startsWith("/public/")) { return req; } /* ========== Authorization required ========== */ const fail = { status: "403", statusDescription: "Forbidden" }; const auth = req.headers.authorization?.[0]?.value; if (!auth) return fail; try { const token = auth.replace("Bearer ", ""); const parts = token.split("."); if (parts.length !== 3) return fail; const [h, p, s] = parts; const header = JSON.parse(b64u(h).toString()); const payload = JSON.parse(b64u(p).toString()); if (payload.iss !== ISSUER || payload.aud !== CLIENT_ID || payload.exp < Date.now() / 1000) { return fail; } const jwks = await getJwks(); const key = jwks.keys.find(k => k.kid === header.kid); if (!key) return fail; const pub = crypto.createPublicKey({ key, format: "jwk" }); const ok = crypto.verify("RSA-SHA256", Buffer.from(`${!h}.${!p}`), pub, b64u(s)); return ok ? req : fail; } catch (e) { return fail; } }; DependsOn: - LambdaAtEdgeRole # You must update the version description when you update the lambda function. LambdaAtEdgeVersion: Type: AWS::Lambda::Version Properties: Description: !Sub "The updated date and time of the lambda function: ${Updated}" FunctionName: !Ref LambdaAtEdge DependsOn: - LambdaAtEdge # ------------------------------------------------------------# # Output Parameters # ------------------------------------------------------------# Outputs: # Lambda LambdaAtEdgeVersionFunctionArn: Value: !GetAtt LambdaAtEdgeVersion.FunctionArn LambdaAtEdgeVersionVersion: Value: !GetAtt LambdaAtEdgeVersion.Version Amazon CloudFront Content Security Policy や レスポンスヘッダーはオリジンからのレスポンスにオーバーライド (上書き) する設定にしています。 AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates a S3 bucket, a CloudFront distribution and DNS records in Route 53. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SubName: Type: String Description: System sub name. (e.g. prod or test) Default: test8 MaxLength: 10 MinLength: 1 AllowedPattern: "^[a-z0-9]+$" DomainName: Type: String Description: Domain name for URL. xxxxx.xxx Default: xxxxx.xxx AllowedPattern: "^(?!-)(?:[a-zA-Z0-9-]{0,62}[a-zA-Z0-9])(?:\\.(?!-)(?:[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]))*$" SubDomainName: Type: String Description: Sub domain name for URL. Default: xxxxx MaxLength: 20 MinLength: 1 CertificateArn: Type: String Description: ACM certificate ARN. AWS Amplify supports the certificate only in us-east-1 region. Default: arn:aws:acm:us-east-1:999999999999:certificate/xxxxxxxxxxxxxxxxxxxxxxxxxxxxx MaxLength: 90 MinLength: 80 AllowedPattern: ^arn:aws:acm:us-east-1:\d{12}:certificate\/.+$ LambdaAtEdgeVersionArn: Type: String Description: Lambda@Edge function version ARN in us-east-1 region. Default: arn:aws:lambda:us-east-1:999999999999:function:example-s3auth-xxxxx:1 MaxLength: 200 MinLength: 40 AllowedPattern: ^arn:aws:lambda:us-east-1:\d{12}:function:.+:.+$ Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "General Configuration" Parameters: - SubName - Label: default: "Domain Configuration" Parameters: - DomainName - SubDomainName - Label: default: "Security Configuration" Parameters: - CertificateArn - LambdaAtEdgeVersionArn Resources: ### 中略 - CloudFront 以外を省略します。 ### # ------------------------------------------------------------# # CloudFront # ------------------------------------------------------------# CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Enabled: true Comment: !Sub CloudFront distribution for example-${SubName}-img Aliases: - !Sub ${SubDomainName}-img.${DomainName} HttpVersion: http2 IPV6Enabled: true PriceClass: PriceClass_200 DefaultCacheBehavior: TargetOriginId: !Sub S3Origin-${SubDomainName}-img ViewerProtocolPolicy: redirect-to-https AllowedMethods: - GET - HEAD - OPTIONS CachedMethods: - GET - HEAD CachePolicyId: !Ref CloudFrontCachePolicy OriginRequestPolicyId: !Ref CloudFrontOriginRequestPolicy ResponseHeadersPolicyId: !Ref CloudFrontResponseHeadersPolicy # ここに、Lambda@Edge 関数の関連付け設定がある LambdaFunctionAssociations: - EventType: viewer-request LambdaFunctionARN: !Ref LambdaAtEdgeVersionArn IncludeBody: false Compress: true SmoothStreaming: false Origins: - Id: !Sub S3Origin-${SubDomainName}-img DomainName: !Sub ${S3Bucket}.s3.${AWS::Region}.amazonaws.com S3OriginConfig: OriginAccessIdentity: "" OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id ConnectionAttempts: 3 ConnectionTimeout: 10 ViewerCertificate: AcmCertificateArn: !Ref CertificateArn MinimumProtocolVersion: TLSv1.2_2025 SslSupportMethod: sni-only Tags: - Key: Cost Value: !Sub example-${SubName} DependsOn: - CloudFrontCachePolicy - CloudFrontOriginRequestPolicy - CloudFrontOriginAccessControl - CloudFrontResponseHeadersPolicy CloudFrontOriginAccessControl: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Description: !Sub CloudFront OAC for example-${SubName} Name: !Sub OriginAccessControl-${SubDomainName}-img OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4 CloudFrontCachePolicy: Type: AWS::CloudFront::CachePolicy Properties: CachePolicyConfig: Name: !Sub CachePolicy-${SubDomainName}-img Comment: !Sub CloudFront Cache Policy for example-${SubName}-img DefaultTTL: 3600 MaxTTL: 86400 MinTTL: 60 ParametersInCacheKeyAndForwardedToOrigin: CookiesConfig: CookieBehavior: none EnableAcceptEncodingBrotli: true EnableAcceptEncodingGzip: true HeadersConfig: HeaderBehavior: whitelist Headers: - Authorization - Access-Control-Request-Headers - Access-Control-Request-Method - Origin QueryStringsConfig: QueryStringBehavior: none CloudFrontOriginRequestPolicy: Type: AWS::CloudFront::OriginRequestPolicy Properties: OriginRequestPolicyConfig: Name: !Sub OriginRequestPolicy-${SubDomainName}-img Comment: !Sub CloudFront Origin Request Policy for example-${SubName}-img CookiesConfig: CookieBehavior: none HeadersConfig: HeaderBehavior: whitelist Headers: - Access-Control-Request-Headers - Access-Control-Request-Method - Origin - referer - user-agent QueryStringsConfig: QueryStringBehavior: none CloudFrontResponseHeadersPolicy: Type: AWS::CloudFront::ResponseHeadersPolicy Properties: ResponseHeadersPolicyConfig: Name: !Sub ResponseHeadersPolicy-${SubDomainName}-img Comment: !Sub CloudFront Response Headers Policy for example-${SubName}-img CorsConfig: AccessControlAllowCredentials: false AccessControlAllowHeaders: Items: - "Authorization" - "Content-Type" AccessControlAllowMethods: Items: - GET - HEAD - OPTIONS AccessControlAllowOrigins: Items: - !Sub https://${SubDomainName}.${DomainName} AccessControlExposeHeaders: Items: - "*" AccessControlMaxAgeSec: 600 OriginOverride: true SecurityHeadersConfig: ContentSecurityPolicy: # 注意。ここで CSP に blob: を入れておかないと後々エラーになります。UI編で説明します。 ContentSecurityPolicy: !Sub "default-src 'self' *.${DomainName} blob:; object-src 'self' blob:;" Override: true ContentTypeOptions: Override: true FrameOptions: FrameOption: DENY Override: true ReferrerPolicy: Override: true ReferrerPolicy: strict-origin-when-cross-origin StrictTransportSecurity: AccessControlMaxAgeSec: 31536000 IncludeSubdomains: true Override: true Preload: true XSSProtection: ModeBlock: true Override: true Protection: true CustomHeadersConfig: Items: - Header: Cache-Control Value: no-store Override: true 続編記事 UI 編では、React アプリからどのようにリクエストを書けばよいか解説します。 React アプリで Amazon Cognito 認証済みユーザーにのみ Amazon S3 静的コンテンツへのアクセスを許可したい -UI編- Amazon Cognito でユーザー認証する React アプリで、Amazon CloudFront 経由の Amazon S3 へのアクセス制御を実装する方法を紹介します。本記事は UI 編です。 blog.usize-tech.com 2026.01.13 まとめ いかがでしたでしょうか? 実運用を考えると、CI/CD 環境までつくれると良いですね。でも一度作ればあまり変更が入らない環境なので、そこまでやるか?とも思いますね。 本記事が皆様のお役に立てれば幸いです。
こんにちは、広野です。 これまでいろいろと React アプリを AWS リソースと連携させるための仕組みをブログ記事にしてきましたが、絶対に使うであろうユースケースを書いていませんでした。以下、順を追って説明したいと思います。 本記事はアーキテクチャ編です。今後、 環境編 、 UI 編 を続編記事として用意します。 背景 こんな状況、ありませんか? アプリにログイン認証はある。(今回は Amazon Cognito 使用) アプリの動的コンテンツ (UI 画面) は AWS Amplify や Amazon S3 で配信されている。 静的コンテンツは Amazon S3 にあり、アプリのログイン済みユーザーにのみ閲覧を許可したい。ただし、全てのコンテンツが認証が必要なわけではなく、パブリックに公開してよいファイルもある。 セキュリティやレスポンスを考慮し、Amazon S3 には Amazon CloudFront をかぶせてアクセスさせたい。 この状況ですと、以下に紹介するアーキテクチャが有用であると考えます。 アーキテクチャ ざっくり全体像は以下になります。Amazon CloudFront に AWS Lambda@Edge 関数を実装することになります。 本記事では、動的コンテンツ置き場の Amplify Hosting や Amazon Cognito については基本言及せず、図の上半分の Amazon CloudFront、AWS Lambda@Edge 関数、および React コードにフォーカスします。 React アプリにログインをすると、Amazon Cognito ユーザープールからトークンをもらえます。アプリから Amazon S3 にある静的コンテンツへのリクエストを行うときにトークンを含め、間に存在する Amazon CloudFront 上で Lambda@Edge 関数によりトークンの正当性をチェックさせるという仕組みです。 以下の AWS 公式ドキュメントやブログ記事のように React アプリログイン機能を AWS Amplify で作成し、その状態になっていることが前提です。 Authenticate existing React application users by using Amazon Cognito and AWS Amplify UI - AWS Prescriptive Guidance Set up user authentication for a React web application by using Amazon Cognito and AWS Amplify. docs.aws.amazon.com AWS Amplify + AWS CDK で人生初のアプリケーションを作ってみた (第1回) AWS Amplify + AWS CDK で人生初のアプリケーションを作ってみました企画の第1回です。本当は序/破/急にしようかと考えてましたが構成的に断念しました。IT業界に従事して約10年になりますが、一度もアプリケーションを作ったことがなく、どのように動いているのかもまだふんわりとしか理解できていませんが、先輩方の記事や生成AIのおかげで何とか形になりました。 blog.usize-tech.com 2025.01.14 アーキテクチャ図の上半分に少し入り込んで説明します。 静的コンテンツへのリクエストを行うとき、そのリクエストの Authorization ヘッダーにトークンを含めます。この動きは一般的にはデフォルトでは実施してくれないので、アプリ側で作り込む必要があります。 UI 編 の記事で説明します。 リクエストは Amazon CloudFront が受け取ります。リクエストをそのまま Amazon S3 に流すのではなく、Lambda@Edge 関数でインターセプトします。 Lambda@Edge 関数は以下の処理をします。 リクエストが CORS プリフライトチェック (OPTIONS メソッド) であれば S3 静的コンテンツにアクセス不要なので、CloudFront に 204 No Content のレスポンスを戻すと、CloudFront がアプリに戻してくれます。このとき、CORS で必要なレスポンスヘッダー Access-Control-Allow-Origin が必要ですが、Amazon CloudFront でレスポンスヘッダーをオーバーライドする設定にしておけば Lambda@Edge 関数側で何もすることはありません。 パブリックに公開可能 (認証不要) な静的コンテンツ (例えば public という名前のフォルダ内にあるとします) へのアクセスであれば、トークンのチェックはせずにそのまま Amazon S3 にリクエストを流します。以降、Amazon S3 からのレスポンスが Amazon CloudFront 経由でアプリに戻ります。 その他のリクエストはトークンのチェックが必要です。Authorization ヘッダーにあるトークンの正当性を Lambda@Edge 関数内でチェックし、正当であれば Amazon S3 にリクエストを流します。不正であった場合は Amazon CloudFront に 403 Forbidden のレスポンスを戻すと、CloudFront がアプリにそれを戻してくれます。 ざっくり恐縮ですがこの方式で Amazon S3 へのアクセスを Amazon Cognito ユーザーのみに制限をかけることができます。 これを実装することによるアプリへのレスポンス影響が心配だったのですが、体感的には許容範囲でした。 続編記事 React アプリで Amazon Cognito 認証済みユーザーにのみ Amazon S3 静的コンテンツへのアクセスを許可したい -環境編- Amazon Cognito でユーザー認証する React アプリで、Amazon CloudFront 経由の Amazon S3 へのアクセス制御を実装する方法を紹介します。本記事は環境編です。 blog.usize-tech.com 2026.01.13 React アプリで Amazon Cognito 認証済みユーザーにのみ Amazon S3 静的コンテンツへのアクセスを許可したい -UI編- Amazon Cognito でユーザー認証する React アプリで、Amazon CloudFront 経由の Amazon S3 へのアクセス制御を実装する方法を紹介します。本記事は UI 編です。 blog.usize-tech.com 2026.01.13 まとめ いかがでしたでしょうか? まだアーキテクチャ編は概要レベルなので、こんな構成で動くよね、という理解をしていただければと思います。具体的な実装面ではハマりどころがあったので、注意点として続編記事で紹介します。 本記事が皆様のお役に立てれば幸いです。
こんにちは、SCSK株式会社 中村です。 先月、SCSKのパートナー企業様との合同の勉強会に参加してきました。趣旨としては互いの若手エンジニアの交流・共同作業を通じたGoogle Cloudに対する知見深化や、生成AIを活用したアプリデプロイの学習です。 勉強会の中でGoogle Cloud + Geminiを使ったアプリ開発を体験し、開発手法の新時代を見たと感じていますのでそちらを中心に活動の内容をご紹介できればと思います。 勉強会の概要 参加者の構成 勉強会は弊社メンバー10名、パートナー様メンバー10名の20名+講師陣という体制で開催され、 参加メンバーは両社のメンバーが均等に割り振られる形で5名1組となり、計4組でワークショップを行いました。 参加者のスキルは日ごろから業務でGoogle Cloudを利用している方から、私のようにオンプレしか経験したことのない人まで様々でしたが 比較的Google Cloudスキルの浅いメンバーが中心となっています。チームごとに課題が出題され最終的に発表を行います。 課題内容 テーマ スキルマップ作成 AI アプリ 目的 : 経歴書からスキルマップを作成する インプットデータ : 経歴書 想定ユースケース 1:中途採用での簡易的なスキル把握 経歴書を読み込まずとも、 どんなスキルを持つ要員か簡単にわかる ようにする。 採用に直接かかわらないが社内のほかメンバへ、 簡単に要員情報を把握 してもらう。 ITの技術スキルはもちろん、管理スキルや過去の実績などもどこまでスキルマップ化するか? 2:自身の経歴書の書き方の参考 対外的にアピールできるような経歴書の書き方となっているかという 自身のスキル の確認 。 自己評価と異なるスキルマップが出てきた場合、改善できないか検討するなどに利用。 入りたいと思っている会社の募集要項と照らし合わせての評価等も面白いか。 課題はチームにより異なりますが、私のチームは上記内容の課題でした。 インプットデータとなる履歴書と、ベースとなる超簡易的なスキルマップ作成アプリは運営側で用意してくれています。 ベースとなるアプリはユースケースに沿った出力を行わないので、私たちは ユースケースに沿うように Google Cloudと生成AIを使ってアプリを改修していく ということが活動の主となります。 活動を始めるにあたって ここからはどのように学習を進めていったかをポイントに絞ってご紹介したいと思います。 まず、今回の勉強会の中で私たちが利用したGoogle Cloudのサービスは以下の通りです。 サービス名 説明 Cloud SQL Google Cloudが提供するフルマネージドのリレーショナルデータベースです。 今回はスキルマップの検索結果を履歴として保存するため利用します。 (しかし結果として履歴機能はほぼ使わない形となりました。) Cloud Run Google Cloud上でコンテナ化されたアプリをインフラの管理無しに実行できるサービスです。 今回作成するスキルマップ作成アプリをデプロイしたり、改修するために使います。 Cloud Load Balancing Google Cloudが提供するネットワークの負荷分散を行うサービスです。 構成が寂しいこともあり、実際の業務システムで作る構成を想定して用意。(無くてもいい) 今回は単一のアプリのため負荷分散の機能は利用しません。 Cloud Storage Google Cloudが提供するストレージサービスです。 インプットデータとなる履歴書の生データや、生成したスキルマップを格納するためのストレージです。 Secret Manager APIキー、証明書、パスワードといった秘密情報を安全に管理するためのサービスです。 デプロイ時にCloud SQLのIDとパスワードを直打ちしないためにSecret Managerに登録して、そこからIDとパスワードを取得する形にします。 Gemini 今回のアプリ開発の肝となる生成AI。バージョンは2.5フラッシュ版を利用しています。 ほぼ全てのアプリの改修をこのGeminiに担ってもらいます。 事前準備 ①VPCネットワークの作成 初めにVPCネットワークをGoogle Cloudのコンソール上から作成します。 コンソール左側のナビゲーションメニューから「VPCネットワーク」を選択し、「+VPCネットワークを作成」のメニューを押下します。 ネットワーク名、サブネット名、リージョン、IPv4範囲を決めてVPCを作成します。その他の項目はデフォルトで大丈夫です。 作成後のネットワーク名やサブネット名は次で使います。 ②Cloud SQLの作成 次にCloud SQLをGoogle Cloudのコンソール上から作成します。 簡易的な機能のみを利用するためEnterpriseエディション、シングルゾーンの料金体系が安い構成で作成します。 細かい設定は運営側で用意された資料を元に設定を行います。(赤枠のような値となるように設定します。) オンプレ環境しか経験したことのない自分にとっては GUIで設定項目を決めるだけで約30分ほどでSQLサーバが作れてしまう というだけで既に驚きです。 SQLの作成が完了したら、作成したSQLを選択して「ユーザ」と「データベース」を新たに作成します。 ”postgres”という名前のものはデフォルトで存在するものですので、追加で任意のユーザとデータベースを作成します。 ③Secret Managerの登録 Secret Managerを開いて、「SQL_USER」「SQL_PASSWORD」という名前でそれぞれ先ほど作成したCloud SQLのIDとパスワードの値を設定します。 ④作業ディレクトリの作成 Google Cloud管理画面からCloud Shellを立ち上げてホームディレクトリ直下に作業用のフォルダを作成します。 Cloud ShellはGoogle Cloudで利用できるブラウザベースのコマンドラインです。 $ mkdir cloud-run-app 作成したディレクトリの中に、運営側で用意してもらった簡易スキルマップ作成アプリの資材を格納します。 下記の資材についてはCloud Shell起動後geminiを立ち上げて、「PythonでCloud RunのWebアプリを作成したい」と言うと ほぼ同じ構成の資材を作ってくれます(中身は全然違いますが) 資材の中身は改修を行うなかでどんどん変わっていくので最初からgeminiに用意してもらう方法でも問題ありません。 ※geminiの起動方法は後述します。 ■資材一覧 資材名 説明 Dockerfile コンテナイメージをビルドするためのファイル main.py スキルマップ作成アプリの本体となるPythonファイル (SQLサーバのIPアドレスやdb名は自分が作成したものに書き換えます) requirements.txt DockerfileがPythonパッケージをインストールするためのリスト templates(フォルダ) →history.html スキルマップ作成アプリにて質問履歴を表示するための設定ファイル →index.html スキルマップ作成アプリの表示構成を決める設定ファイル 初期スキルマップの確認 現状のスキルマップがどのようなものか確認するために用意されたアプリのデプロイを行います。 ※[cloud-run-app]は任意のアプリ名、[vpc][subnet]は自身がVPCネットワークを作成した際の名称を指定するようにしてください。 $ cd cloud-run-app $ $ gcloud run deploy [cloud-run-app] --region asia-northeast1 --network=[vpc] --subnet=[subnet] --vpc-egress=private-ranges-only --port 8080 --set-secrets SQL_USER=SQL_USER:latest,SQL_PASSWORD=SQL_PASSWORD:latest デプロイのコマンドを実行するとソースコードの場所を確認されることがありますが 最初にcdでソースファイルが格納されている場所に移動済みですのでそのまま何も入力せずにEnterを押してもらって大丈夫です。 (チームの別のメンバーは何も聞かれずにデプロイ完了したりすることもあるみたいで、ここら辺の違いはよく分かりませんでした) デプロイが無事完了すると、作成されたURLがプロンプト内に表示されます。 デプロイが完了したら、Cloud Runから「サービス」を選択するとリストの中に先ほどデプロイした[cloud-run-app]の名前のアプリがあると思いますので選択します。開いた画面にURLが記載されていると思いますので、そちらをクリックするとデプロイしたアプリを開くことが出来ます。 初期状態のスキルマップ作成アプリ 上の図が運営側で用意してくれたスキルマップ作成アプリです。(※表示されている氏名や経歴は架空の社員を想定したものです。) 簡素な作りで、 年齢の表記の仕方もバラバラだったりするのでここら辺の体裁も整えたい ですね スキルマップを作成したい人のチェックボックスにチェックを入れてページ下部にあるスキルマップ作成のボタンを押下するとExcel形式のスキルマップが作成されます。 上記が初期状態のスキルマップです。 スキル項目について、チェックボックスで選択した人がどれくらいのレベル感なのかをGeminiが◎、〇、△、-といった記号で判断して入力してくれています。しかし これだけではなぜ〇なのか、なぜ△なのかという根拠が分かりません 。 インプットデータの履歴書には 保有する資格なども記載があるため、そちらもレベル感を主張する材料として出力できるとよい です。 といった具合に色々改良の余地がありそうなので、これを Geminiで改良していきます 。 アプリの改修をGeminiでやってみる 必要な準備ができたので、ここからはGeminiを使ってスキルマップ作成アプリを改修していきます。 Cloud Shellにてアプリ資材を配置しているフォルダに移動してGeminiを起動します。 Geminiの起動は任意の場所で”gemini”と打つだけ です。簡単! $ cd cloud-run-app $ $ gemini 起動すると上記のような画面になります。 対話式のアプリ改修 さっそくGeminiにアプリの改修を依頼してみます。 試しに出力されるスキルマップについて、一人ひとりシートを分けて結果を出力することが可能か聞いてみました。 いかがでしょうか? 私は 「スキルマップを出力する人が複数選択されている場合、一人ひとりシートを分けて結果を出力することは可能ですか?」とだけ質問 しただけなのですが、 Geminiは自分でフォルダ内のmain.pyの中身を確認して、改善案を2つ提示 してくれました。また、 案②のほうが見やすいという意見をこちらに伝えた上で案②に変更しますか?と提案 してくれました。非常に優秀な営業マンのようです。 特に懸念はないためGeminiの提案通り案②への変更で進めます。 本当に変更していいのか?という最終確認が入るので、「1. Yes,allow once」を選択し、Enterを押下します。 そうして、 ものの5分ほどで修正が完了 しました。 従来のアプリ改修では考えられないスピード です。 そもそも 自分でツールを編集するということすらやらなくても変更が完了 出来てしまいました。 ただ、これだけではファイルの中身を変えただけなので再度デプロイしてあげる必要があります。 デプロイが10分ほど時間がかかりますが、それを考慮してもとても短い時間 ですね。 再デプロイを終えて、改めてCloud Runからアプリを起動、適当に人を選択してファイルを出力してみます。 そして出力されたファイルがこちら。 赤枠の通りしっかり 個人ごとにシートが分かれて結果を出力してくれています ね。 もしこれを自分でやろうものなら、pythonのコマンドを調べてコードの追記位置を検討して実装してみて問題があればエラー箇所を特定して、、と修正を完了するために 私であれば 2人/日はかかってしまうであろうものがGeminiによって5~10分で完了してしまいました 。 最終的な成果物 その後のスキルマップの改修については基本的には上記で紹介したようにやりたいことをGeminiに伝えて、 Geminiが提案してくれた内容が要望通りのものであれば承諾して改修を進めることの繰り返しです。 そんな感じで進めて、成果物となるスキルマップの改修に加えて、生成アプリも最終的には以下のような感じに変更しました。 ~変更点~ ・名前をクリックすると履歴書の生データが表示されるようにリンクを設定 ・年齢の表記方法が”〇歳”と統一されるように修正 ・ページの配色をかわいい感じに変更(Geminiにかわいくして。とお願いしたら上記の青ベースになりました。) ・スキルレポートの生成を開始したら進捗率が表示されるように修正 ・スキルレポートの生成時間が短縮されるように修正(5~10分ほど→20秒ほどに短縮) システム構成図 最終的なアプリの構成図は以下の通りです。 Cloud RunのフロントにCLB(Cloud Load Balancing)を配置していますが、勉強会の最後に発表する成果物の資料として Cloud RunとCloud StorageとGeminiだけだと構成図的にも寂しいなとなったので、 実際の業務システムで作る構成を想定してCLBを追加しました。それ以外は前述した通りの処理の流れとなっています。 さいごに 以上がGoogle CloudとGeminiを使ったアプリ開発体験の紹介となります。 アプリケーションエンジニアの方ではなくとも、自分でバッチやマクロなどを作った経験のある方であれば Geminiを使った対話式のアプリ開発が、従来の方法とは一線を画すものであることは何となくイメージしてもらえたのではないでしょうか? ただ、 Geminiによる改修がいつも上手くいくかというと上手くいかないことも多い です。 そんな時でもCloud Runの[オブザーバビリティ]>[ログ]というところに大抵エラー出力などが出ていますので、 その エラーの記述を丸々コピーして、これもGeminiに投げて原因を聞くとほとんどのことは解消してくれました 。 他にはほぼ 全ての変更をGeminiに任せているので、エラーが解消できなかった時などは本当にどこが悪いのかという検討が自分でつけられなくなってしまうことがありました 。そうすると結局Geminiを使って更なる改修を加えるか、前のバージョンに切り戻すことが必要になってきます。 とはいえ、やはり開発経験がない人間でも対話ベースで作りたいアプリが作れるというのは非常に大きな魅力です。 さらにAIによる開発スピードの高速化というのも今回の勉強会で感じた大きなポイントでした。 今後求められるエンジニア像というものは、 AIとの協調をベースとしたスキルセットに変化していくであろうし、私たちも変化しなければいけない ということを強く感じています。 私自身も自己研鑽としてクラウドや生成AIについて学習して 時代にあったエンジニアとして活躍できるように頑張っていきたいと思います。 (とりあえず初めの一歩としてGoogle Cloud Leaderの資格を取得しました。) 最後までお読みいただき、ありがとうございました。
こんにちは、SCSKの谷です。 これまでに3大クラウドの各サービスをMackerelのクラウドインテグレーションを利用して監視を実装する記事を投稿してきました。 AWS: Mackerel で AWS のサーバーレスサービスを監視してみた – TechHarmony Azure: MackerelでAzure環境を監視してみた! – TechHarmony Google Cloud: MackerelでGoogle Cloudを監視してみた – TechHarmony 最後に総まとめということで、3大クラウドのMackerel監視(クラウドインテグレーション)を比較していきます! そもそもMackerelのクラウドインテグレーションとは? これまでの記事の中で各クラウドインテグレーションを利用して監視を実装していましたが、そもそもMackerelのクラウドインテグレーションについて説明していなかったので、簡単に説明させていただきます。 Mackerelのクラウドインテグレーションとは、AWS/Azure/Google Cloudと連携し、それぞれのクラウド環境を「Mackerel上のホスト」として一元監視できる機能です。 ■基本構造 Mackerel側から各クラウドの監視サービスのAPIを利用してホストの情報及びメトリクスを取得 ・ポーリング間隔:5分 ■利用方法 MackerelからAPIを使用するために、それぞれのクラウドに合わせた認証方法を利用します。 AWS:IAMロールで連携 + ポリシー付与 Azure:サービスプリンシパル設定 + ロール割当 Google Cloud:サービスアカウント設定 + ロール割当 クラウドインテグレーションで監視可能なクラウドサービス比較 ここでは、Mackerelのクラウドインテグレーションで監視可能なサービスについて比較していきます。 合計サービス比較 現在Mackerelのクラウドインテグレーションで監視可能なクラウドサービスの合計はそれぞれ以下の通りです。 AWS Azure Google Cloud 合計サービス数 27 11 3 クラウドインテグレーションで監視できるサービスの数を比べると、クラウド利用者が多い AWS が、他のクラウドを大きく引き離していることがわかります。 サービスカテゴリ比較 各クラウドサービスのカテゴリごとの監視可能サービスの対応状況は以下の通りです。 カテゴリ分けはある程度各クラウドサービスのカテゴリ分けに沿っています。よく利用されるサービスのカテゴリについては太字にしています。 カテゴリ AWS Azure Google Cloud コンピューティング ◎ ◎ 〇 コンテナ 〇 × × データベース ◎ ◎ 〇 ネットワーク ◎ 〇 × ストレージ 〇 〇 × 分析 ◎ × × アプリケーション統合 〇 × × ビジネスアプリケーション 〇 × × セキュリティ 〇 × × 開発者ツール 〇 × × 管理 〇 × × 上記以外のカテゴリ × × × ※3つ以上対応している場合は◎ AWSは監視可能なサービス数が非常に多く、ほぼすべてのカテゴリで1つ以上のサービスに対応しています。さらに、利用頻度が高いカテゴリでは3つ以上のサービスを監視できるものもあり、幅広い選択肢を提供しています。特筆すべきは、AWSのみコンテナ関連のサービス監視に対応している点です。 一方、Azureはコンピューティング系とデータベース系で3つ以上のサービスに対応しているものの、基本的にはよく利用されるサービスに限定されています。 Google Cloudはさらにシンプルで、コンピューティング系とデータベース系のみ監視可能であり、最低限のサービス対応にとどまっています。 クラウドインテグレーションで取得可能なメトリクス ここでは、Mackerelのクラウドインテグレーションを利用して、仮想サーバー系サービスで取得できるメトリクス数と項目を比較していきます。 メトリクス数 現在Mackerelのクラウドインテグレーションで取得可能な仮想マシンサービスのメトリクス数合計はそれぞれ以下の通りです。 比較として、各クラウドの監視サービスで取得可能なメトリクス数も記載しています。 AWS Azure Google Cloud Mackerel 21 11 22 クラウド監視サービス 29 64 179 Mackerelでは、CloudWatchなどのクラウド監視サービスからすべてのメトリクスを取得するのではなく、利用頻度の高いメトリクスに絞って収集しているように見えます。特にGoogle CloudやAzureの場合、提供されるメトリクス数が非常に多いため、Mackerel側であらかじめ選定されたメトリクスのみが管理画面に表示されることで、ユーザーは膨大なメトリクスの中から必要なものを探す手間がなく、直感的でわかりやすい監視設定が可能になっていると感じました。 メトリクス項目 現在Mackerelのクラウドインテグレーションで取得可能な仮想マシンサービスのメトリクス項目はそれぞれ以下の通りです。 ■共通するメトリクス AWS Azure Google Cloud CPU CPUUtilization CPUCreditUsage CPUCreditBalance CPUSurplusCreditBalance CPUSurplusCreditsCharged Percentage CPU CPU Credits Remaining CPU Credits Consumed utilization Disk DiskReadOps DiskWriteOps DiskReadBytes DiskWriteBytes Disk Read Operations/Sec Disk Write Operations/Sec Disk Read Bytes Disk Write Bytes read_bytes_count write_bytes_count read_ops_count write_ops_count throttled_read_bytes_count throttled_write_bytes_count throttled_read_ops_count throttled_write_ops_count Network NetworkIn NetworkOut NetworkPacketsIn NetworkPacketsOut Network In Network Out Network In Total Network Out Total received_bytes_count sent_bytes_count received_packets_count sent_packets_count ■クラウド個別のメトリクス クラウド 分類 メトリクス 説明 AWS Status StatusCheckFailed_Instance StatusCheckFailed_System StatusCheckFailed EC2インスタンスやシステムの正常性を監視するためのステータスチェック結果 Disk (EBS) EBSReadOps EBSWriteOps EBSReadBytes EBSWriteBytes EBSIOBalance% EBSByteBalance% AWS EBS(Elastic Block Store)のI/O性能とバランスを監視するメトリクス群 Azure VM Availability Metric VmAvailabilityMetric Azure VM の稼働可否を示すメトリクス Google Cloud Uptime instance/uptime Compute Engineが起動してからの累積稼働時間を示すメトリクス ミラーリング パケット Mirroring bytes Mirroring packets Mirroring packets dropped ミラーリングされたトラフィック量とパケット数、そしてドロップされたパケット数を示すメトリクス Firewall dropped_bytes_count dropped_packets_count Google Cloud の VPC ファイアウォールでドロップされたトラフィック量(バイト数)とパケット数を示すメトリクス ■共通して取得しているカテゴリ どのクラウドでも、CPU・Disk・Network関連のメトリクスは必ず取得しています。 どのクラウドでもクラウドインテグレーションを使用した場合メモリのメトリクスを取得することができず、仮想マシンにエージェントを入れる必要があります。Mackerelとしてはメモリのメトリクスはエージェントから取得する思想なのかなと感じました。 ■クラウドごとの特色 共通メトリクス以外は、それぞれのクラウド特有の指標が追加されています。 AWS:EBSのI/O性能やステータスチェック(可用性) Azure:VMの可用性を示す VmAvailabilityMetric Google Cloud:ファイアウォールのドロップ数やパケットミラーリングなど、ネットワークセキュリティ関連のメトリクス クラウドインテグレーションの設定手順 最後に各クラウドのMackerelクラウドインテグレーションの設定手順を比較していきます。 詳しい導入手順については、各ブログで紹介していますので、そちらをご覧ください。 設定手順の難易度 今回は以下の指標で導入手順の難易度を比較しています。 設定方法の種類 複数の設定パターン(GUI、CLI)が用意されているかどうか 手順数(GUI) GUIで設定する場合、完了までに必要なステップ数はどれくらいか 設定手順の種類 複数の手順で設定可能か 所要時間(GUI) GUIで設定した場合、初期設定にどれくらい時間がかかるか 上記をまとめた結果は以下の通りです。 AWS Azure Google Cloud 設定方法 △ GUI 〇 GUI、CLI 〇 GUI、CLI 手順数(GUI) 〇 13 △ 16 △ 15 設定手順の種類 〇 ・IAMロール(推奨) ・Access Key IDと Secret Access Key 〇 ・Azure CLI 2.0 ・Azure Portal 〇 ・Cloud SDK ・Cloud Console 所要時間(GUI) 〇 3分2秒 × 7分2秒 △ 4分46秒 今回、AWS・Azure・Google Cloudのクラウドインテグレーション設定を実際に試してみました。その結果、設定のしやすさや所要時間に違いがあり、次のような印象を持ちました。 ・AWS 最もスムーズに設定できました。手順数も少なく、公式ドキュメントを参照しながら進めても特に詰まる箇所はありませんでした。 全体的に直感的で、短時間で設定完了できる印象です。 ・Azure 設定にやや時間がかかりました。公式ドキュメントで指定されている項目の場所をAzureポータル上で探すのに手間取ったことと、手順自体が多かったことです。その分、設定完了までに時間がかかる印象でした。 (慣れてないだけかもしれませんが。。。) ・Google Cloud 設定の大部分はスムーズでしたが、APIライブラリで必要なAPIを有効化する作業に少し手間取りました。公式ドキュメントに記載されて いるAPI名と、Google Cloud Console上で表示される名前が異なる場合があり、確認に時間がかかりました。 ただ、それ以外の手順は問題なく進められました。 総じて、クラウドインテグレーションの設定は比較的簡単に行えると感じました。 AWS・Azure・Google Cloudそれぞれに特徴はありますが、公式ドキュメントを参照すれば、どのクラウドでも大きな障壁なく設定を完了できます。 おわりに 記事は以上になります。いかがでしたでしょうか? 詳細が気になった方は 公式ヘルプ をご確認ください。 今回は AWS、Azure、Google Cloud における Mackerelのクラウドインテグレーションを比較し、 「監視可能なサービス」、「取得できるメトリクス」、「設定手順」といったポイントを整理しました。 整理したことによりMackerelのクラウドインテグレーションでも、クラウドごとに細かな違いがあることが改めて分かりました。 Mackerelクラウドインテグレーションのメリットは、各クラウド上のリソースをMackerelのホストとして一元管理できることです。 これにより、複数クラウドを利用していても、統一された監視基盤を構築できます。 現状クラウドサービスによって利用できるレベルに差があるので、将来的にはどのクラウドサービスでも同じレベルでクラウドインテグレーションを利用できるようになると、さらに便利になると感じました。 これまで、部署内で Mackerelのクラウドインテグレーションに関する記事を投稿してきましたが、ひとまず今回の記事で一区切りとなります。最後までお読みいただき、ありがとうございました! 今後も、新しい気づきや皆さまに役立つ情報があれば、随時記事を更新していきます。 次回の投稿をぜひお楽しみに!
こんにちは。 今更ながら Amazon WorkSpaces で SAML 連携を実施する機会があり、いろいろ触ってみましたので、 今回は Microsoft Entra ID を利用した SAML 連携設定を紹介したいと思います。 構成イメージ 今回設定するSAML認証のイメージ図です。 Entra IDをIAM IDプロバイダーとして登録し、WorkSpacesディレクトリの多要素認証方法として指定することで、多要素認証でEntra ID認証を利用することができるようになります。 図の中では省略していますが、AD認証にはEC2で構築した検証用ADサーバを利用するため、AD Connectorを作成して連携先ADに検証用ADサーバを指定しています。また、WorkSpacesはPersonal(個人)を利用します。 本記事では、検証用AD/AD Connector/WorkSpacesの基本的な設定が実施された環境に対して、Entra IDを用いた多要素認証設定を追加設定する手順を説明します。 設定手順 今回設定する手順は以下の通りです。 AWSとAzureのコンソールを行き来するので、分かりやすいように()内に操作するコンソールを書いています。 ①エンタープライズアプリケーション作成 (Azure) ②SAML基本設定/フェデレーションメタデータXMLダウンロード(Azure) ③SAML ID プロバイダー作成 (AWS) ④IAMロール作成/インラインポリシー設定(AWS) ⑤SAML認証応答のアサーション設定 (Azure) ⑥フェデレーションのリレー状態構成 (Azure) ⑦アプリケーションへのユーザー割り当て/リンクコピー(Azure) ⑧WorkSpacesディレクトリでSAML2.0統合を有効化 (AWS) 以上の設定を順を追って説明していきます。 ①エンタープライズアプリケーション作成 (Azure) まずはEntra IDにSAML認証用のエンタープライズアプリケーションを追加していきます。 Azure管理コンソールでEntra IDの画面を開き、エンタープライズアプリケーションを新規作成します。 アプリケーションの新規作成画面で「独自アプリケーションの追加」を選択し、任意のアプリ名を入力します。 「アプリケーションでどのような操作を行いたいですか?」の箇所については、一番下の「ギャラリー以外」を選択して アプリケーションを作成します。 ②SAML基本設定/フェデレーションメタデータXMLダウンロード(Azure) エンタープライズアプリケーションの作成が完了したら、作成したアプリケーションの「シングルサインオン」画面を開きます。 デフォルトで「無効」となっているシングルサインオン方式を「SAML」に設定します。 続いて、以下のURLからAWS提供のメタデータXMLをダウンロードします。 https://signin.aws.amazon.com/static/saml-metadata.xml ダウンロードしたメタデータXMLをアップロードして、「保存」を押下します。 SAML設定画面に戻ったら、「SAML証明書」欄のダウンロードリンクから、メタデータXMLファイルをダウンロードします。 ここでダウンロードしたXMLファイルを用いて、AWS側のIDプロバイダー設定を実施します。 ③SAML ID プロバイダー作成 (AWS) 続いてはAWS側の設定となります。 AWSマネジメントコンソールを開いて、IAM>IDプロバイダを開きます。 「プロバイダを追加」を押下して以下の通り設定します。 ・プロバイダのタイプ:SAML ・プロバイダ名:任意 ・メタデータドキュメント:手順②でダウンロードしたメタデータXMLファイルを選択 設定出来たらIDプロバイダを作成します。 ④IAMロール作成/インラインポリシー設定(AWS) 続いて、SAML認証で利用するIAMロールを作成します。 IAM>ロールを開き、「ロールを作成」を押下します。 作成画面が開いたら、以下の通り設定し、「次へ」を押下します。 ・信頼されたエンティティタイプ:SAML2.0フェデレーション ・SAML2.0ベースのプロバイダー:手順③で作成したIDプロバイダ ・許可されるアクセス:プログラムによるアクセスのみを許可する ・属性:SAML:sub_type ・値:persistent その他の設定はデフォルトのまま、任意のロール名で一度ロールを作成します。 ロールが作成出来たら、ロールの信頼関係設定で、信頼ポリシーのActionに”sts:TagSession”を追加し、保存します。 ロールの設定が完了したら、最後に以下の通りインラインポリシーを追加します。 ポリシー内の対象リージョン/アカウントID/ディレクトリIDは、それぞれご自身が利用する予定のものに置き換えてください。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "workspaces:Stream", "Resource": "arn:aws:workspaces:<対象リージョン>:<対象AWSアカウントID>:directory/<WorkSpacesディレクトリID>", "Condition": { "StringEquals": { "workspaces:userId": "${saml:sub}" } } } ] } ⑤SAML認証応答のアサーション設定 (Azure) さて、ここまできたらAzure管理コンソールに戻って、作成したアプリケーションへAWS側の情報を追加していきます。 手順①で作成したアプリケーションの「シングルサインオン」画面を開き、「属性とクレーム」を編集します。 編集画面が開いたら、属性を以下の通り編集します。 ※デフォルトで設定されている属性/クレームは削除してください。 クレーム名 名前識別子の形式 値 一意のユーザー識別子 (名前 ID) 永続的 user.mailnickname [nameid-format:persistent] https://aws.amazon.com/SAML/Attributes/Role – (デフォルト) ④で作成したロールのARN,③で作成したプロバイダのARN (arn:~,arn:~のようにカンマ区切りで記載する) https://aws.amazon.com/SAML/Attributes/RoleSessionName – (デフォルト) user.mailnickname https://aws.amazon.com/SAML/Attributes/PrincipalTag:Email – (デフォルト) user.mail ⑥フェデレーションのリレー状態構成 (Azure) アプリケーションの「シングルサインオン」画面に戻り、SAML基本設定を編集します。 編集画面が開いたら、リレー状態に利用リージョンのリレーステートURLを記載します。 https://relay-state-region-endpoint/sso-idp?registrationCode=registration-code 各リージョンのリレーステートエンドポイントは以下のAWSドキュメントを参照してください。 https://docs.aws.amazon.com/ja_jp/workspaces/latest/adminguide/setting-up-saml.html#configure-relay-state ⑦アプリケーションへのユーザー割り当て/リンクコピー(Azure) SAML認証を利用するEntra IDユーザー(もしくはグループ)を作成したアプリケーションに割り当てていきます。 ※ここで割り当てたユーザー/グループのみSAML認証を突破できます。 (割り当ててていないユーザーの場合、認証時にエラーになります。) 作成したアプリケーションの「ユーザーとグループ」画面を開き、「ユーザーまたはグループの追加」からWorkSpacesを利用するユーザー/グループを追加します。 ユーザ/グループの追加が完了したら、以下のURLへアクセスします。 https://myapps.microsoft.com/ アクセスしたら、作成したアプリケーションのリンクをコピーして、メモ帳等に控えておきます。 ⑧WorkSpacesディレクトリでSAML2.0統合を有効化 (AWS) 最後にWorkSpacesのディレクトリ設定でSAML2.0統合を有効化していきます。 AWSマネジメントコンソールでWorkSpaces画面を開き、SAML連携を実施するディレクトリを開きます。 ディレクトリ画面が開いたら、認証設定を編集してきます。 認証設定画面が開いたら、「SAML 2.0 アイデンティティプロバイダーの編集」を押下します。 「SAML 2.0 認証の有効化」にチェックを入れ、ユーザーアクセスURLに以下のURLを入力します。 https://myapps.microsoft.com/signin/<手順⑦でコピーしたアプリケーションのリンクのsignin以降> 記述イメージ:https://myapps.microsoft.com/signin/******?tenantId=******* URLの入力ができたら設定を保存します。 以上で、AWS/Entra IDのSAML連携設定およびWorkSpacesディレクトリの多要素認証設定は完了です。 動作確認 多要素認証の設定が完了したので、動作確認をしてみます。 動作確認前にADユーザーおよびWorkSpacesワークスペースを作成してください。 ただし、Entra IDを多要素認証で利用する場合、ADユーザーのユーザー名およびメールアドレスがEntra ID側のユーザーと一致している必要がありますので、ご注意ください。 まずはクライアントPCにインストールしたWorkSpacesクライアントを起動します。 すると、ID/パスワードの入力画面ではなく、「サインインを続行」のボタンが表示されるため、押下します。 ボタンを押下するとEntra IDのログイン画面へリダイレクトされるため、認証情報を入力します。 認証に成功するとWorkSpacesクライアントが再度起動します。 起動後、パスワードの入力画面が表示されたら、パスワードを入力します。 ※この際、Entra ID認証で利用したユーザー名が自動で入力されます。 パスワードを入力してサインインすると、無事デスクトップ画面が表示されました。 さいごに ここまで、Entra IDのSAML認証をAmazon WorkSpacesの多要素認証として利用する手順を説明しましたが、いかがでしょうか? 最後に、試してみた感想と注意すべき点について述べて終わりたいと思います。 ・Azure(Entra ID)と組み合わせることでWorkSpacesのセキュリティを向上させることができる ⇒セキュリティ向上はもちろん、他のクラウドサービスと連携できることがAWSの強みだと再実感しました。 ・仕様上の落とし穴に注意 ⇒Entra IDのユーザー名/メールアドレスとOS認証で利用するADユーザーのユーザー名/メールアドレスが一致している必要があるため、Entra IDとAD間の同期/連携を行っていない場合は注意が必要です。 以上です。ここまで読んでいただきありがとうございました。
こんにちは、広野です。 件名の件で地味に悩んだので、書き残します。AWS 公式ドキュメントで明確な答えを見つけられず、生成 AI に助けてもらいました。 やりたかったこと セキュリティ対策で、Amazon API Gateway HTTP API の特定のレスポンスヘッダーを任意の値にオーバーライドしたかったのですが、 AWS CloudFormation による設定の書き方 がわからず。 REST API だと以下のドキュメントに書いてあります。 API Gateway での REST API パラメータマッピングの例 - Amazon API Gateway Amazon API Gateway で API メソッドリクエストからメソッドレスポンスパラメータへのデータマッピングを設定する docs.aws.amazon.com HTTP API だと具体的な記述方法が見当たりませんでした。CloudFormation のリファレンスの方にも無く。 API Gateway で HTTP API の API リクエストとレスポンスを変換する - Amazon API Gateway Amazon API Gateway HTTP API の API リクエストとレスポンスを変更するためのパラメータマッピングを設定する方法について説明します。 docs.aws.amazon.com AWS CloudFormation テンプレート抜粋 結局、以下のように書くことで設定できました。インラインでコメントします。 統合 (Integration) の部分にオーバーライドしたいヘッダーの内容を書きます。 # ------------------------------------------------------------# # API Gateway # ------------------------------------------------------------# HttpApi: Type: AWS::ApiGatewayV2::Api Properties: Name: sample-send-function Description: HTTP API Gateway to send xxxxx ProtocolType: HTTP CorsConfiguration: AllowCredentials: false AllowHeaders: - "*" AllowMethods: - POST - OPTIONS AllowOrigins: - !Sub https://xxxxx.xxx ExposeHeaders: - "*" MaxAge: 600 DisableExecuteApiEndpoint: false IpAddressType: dualstack Tags: Cost: xxxxx HttpApiIntegration: Type: AWS::ApiGatewayV2::Integration Properties: ApiId: !Ref HttpApi IntegrationMethod: POST IntegrationType: AWS_PROXY IntegrationUri: !GetAtt Lambda.Arn # 関連付ける Lambda 関数の ARN CredentialsArn: !GetAtt ApiGatewayLambdaInvocationRole.Arn # Lambda 関数を invoke するロール PayloadFormatVersion: 2.0 TimeoutInMillis: 5000 ### ここにレスポンスヘッダーを書く ### ResponseParameters: "200": # HTTP レスポンスステータスごとに書かないといけないので完璧ではない。 ResponseParameters: # 二重に ResponseParameters が登場するが、これで正しい。 - Destination: "append:header.X-Content-Type-Options" # Destination が変更方法とパラメータ名。 Source: nosniff # Source が値。わかりにくい!! - Destination: "overwrite:header.Cache-Control" Source: "no-store, max-age=0" - Destination: "overwrite:header.Content-Security-Policy" Source: "frame-ancestors 'none';" - Destination: "overwrite:header.X-Frame-Options" Source: DENY - Destination: "overwrite:header.Strict-Transport-Security" Source: "max-age=31536000; includeSubDomains" HttpApiRoute: Type: AWS::ApiGatewayV2::Route Properties: ApiId: !Ref HttpApi RouteKey: POST /send Target: !Sub integrations/${HttpApiIntegration} HttpApiStage: Type: AWS::ApiGatewayV2::Stage Properties: ApiId: !Ref HttpApi AutoDeploy: true StageName: $default 注意事項 書いたコメントと重複しますが、以下、ハマり箇所でした。 HTTP レスポンスステータス単位で書かないといけない。エラーステータスのときも書くとなると、うーん。きちんとやろうとすると Amazon CloudFront をかぶせた方が正解なんでしょうね。 ResponseParameters の項目が二重に出てくるのですが、それで正しいです。気持ち悪いですが・・・。 Destination が変更方法とパラメータ名、Source が値、というのが非常にわかりにくいです。このネーミングではやってみないとわからないです。 X-Content-Type-Options ヘッダーについては、overwrite (上書き) ではなく append (追加) にしないとエラーになりました。 結果 AWS マネジメントコンソールで設定を確認したところ、以下のように反映されました。API の統合のメニューにあります。 呼び出し元アプリ (ブラウザ) の方にも、設定したレスポンスヘッダーが返されていることが確認できました。めでたし、めでたし。 まとめ いかがでしたでしょうか。 今回は小ネタでしたが、同じことでハマる人がいるかもしれないと思い書いておきました。 本記事が皆様のお役に立てれば幸いです。