TECH PLAY

SCSKクラウドソリューション

SCSKクラウドソリューション の技術ブログ

1226

こんにちは。SCSKの井上です。 この記事では、New RelicのInfrastructure エージェント導入後に、インフラ基盤を監視するための画面の見方を解説します。CPU、メモリ、ネットワークトラフィックなどの観測方法を理解することで、インフラ基盤のボトルネックを把握できるようになります。 はじめに アプリケーションを安定して動作させるためには、インフラ基盤を正常に稼働させることが不可欠です。ハードウェア性能に関するデータを観測し、インフラ関連の情報を一元管理することで、システム全体を把握し、健全性を高めることが安定したサービス提供につながります。この記事を読んで、Infrastructure機能を少しでも理解いただけると幸いです。 Infrastructureエージェントの導入方法については、過去の記事からご確認いただけます。 【New Relic】Infrastructureエージェントのインストールと設定(Linux版) この記事では、New RelicのInfrastructureエージェントの導入方法について解説します。手順を説明する前に、よくある疑問に触れていますので、理解を深めてから実際の手順に挑んでいきます。 blog.usize-tech.com 2026.01.14   InfrastructureのUI構成 この画面でInfrastructureの見方を解説します。サーバーやコンテナ、といった基盤全体の状態を俯瞰できます。アプリケーションのパフォーマンスを追うだけでは見えない、ホストレベルのリソース状況やイベント履歴、インベントリ変化などを統合的にチェックできるため、日々の運用監視やトラブルシューティングに欠かせないUIです。 主要部分UI Infrastructureの機能は主要3セクションに分かれています。Hostsはリソース監視、Inventoryは構成管理、Eventsはトラブルシューティングに活用など、それぞれインフラ基盤を観測する上での必要な機能が備わっています。   セクション 概要 Overview インフラUIの主要ページ。 ・ Hosts :ホストの状態を一覧表示 ・ Kubernetes :K8sインテグレーションのデータ ・ Network :ネットワークパフォーマンス監視 ・ Inventory :構成情報の確認(アップグレードや設定ドリフト) ・ Events :重要なシステムアクティビティのタイムライン Integrations インフラ統合のテレメトリを表示。 ・クラウドサービス(AWS、Azure、GCP、OCI) ・サードパーティサービス(Apache、Cassandra、RabbitMQなど) Settings システム設定関連。 ・ Alerts :アクティブなインシデントを含むアラート概要   インフラストラクチャ監視 UI の紹介 | New Relic Documentation An introduction to the New Relic infrastructure monitoring UI pages. docs.newrelic.com   全ホストのサマリを確認したい場合 この画面ではInfrastructure エージェントが導入されたホストのメトリクスデータを確認することができます。 対象画面 利用シーン サマリ情報 最近の負荷変動や異常をすばやく確認したいとき システム情報(CPU/メモリ等) 長期的な傾向や履歴を分析したいとき Network情報 ネットワークのボトルネックや負荷分散の必要性を判断したいとき プロセス情報 高負荷プロセスや異常な動作を特定したいとき ディスク情報 ディスク容量不足やI/O異常を早期に検知したいとき   他のメトリクスを確認したい場合 New Relicでは、調査に必要なさまざまなメトリクスを収集しています。その他のメトリクスを確認したい場合は、画面の赤枠で示したプルダウンメニューから選択できます。執筆時点で確認可能なメトリクス一覧も参考として表で記載します。   New Relicで観測できるメトリクス情報一覧 カテゴリ メトリクス名 説明 CPU CPU (%) CPU全体の使用率   CPU user (%) ユーザープロセスによるCPU使用率   CPU system (%) システムプロセスによるCPU使用率   CPU i/o wait (%) I/O待ちによるCPU使用率   CPU idle (%) CPUアイドル時間の割合   CPU steal (%) 順番待ちしているCPU時間の割合 ロードアベレージ Load average 1 min 直近1分間の平均負荷   Load average 5 min 直近5分間の平均負荷   Load average 15 min 直近15分間の平均負荷 メモリ Memory used (%) メモリ使用率   Memory free (%) メモリ空き率   Memory free bytes 空きメモリ容量   Memory total bytes メモリ総容量   Memory used bytes 使用メモリ容量 スワップ Swap free bytes 空きスワップ容量   Swap total bytes スワップ総容量   Swap used bytes 使用スワップ容量 ディスク Disk free bytes 空きディスク容量   Disk free (%) 空きディスク率   Disk total bytes ディスク総容量   Disk used bytes 使用ディスク容量   Disk used (%) ディスク使用率   Disk utilization (%) ディスク全体利用率   Disk read utilization (%) 読み取り時のディスク利用率   Disk write utilization (%) 書き込み時のディスク利用率   Disk reads per second 1秒あたりのディスク読み取り回数   Disk writes per second 1秒あたりのディスク書き込み回数 アプリケーション Application response time アプリケーション応答時間(APM有効時のみ)   Application throughput アプリケーションのスループット(APM有効時のみ)   Application error rate アプリケーションのエラー率(APM有効時のみ)   個々のホストを確認したい場合 全ホストを一覧で確認し、特定のホストだけリソース使用率が高いことがわかったため、ホスト数を絞りたい場合などは、画面右上の 「 Filter 」 をクリックして対象ホストを絞り込むことができます。   個々のホストを1つの画面で確認したい場合 特定の1つのホストだけを確認したい場合は、Summary欄より該当のホストをクリックすることで、そのホストに関する観測データを確認することができます。 1.画面下部「Summary」欄にホスト情報が表示されています。表示したいホストをクリックします。 2.該当のホスト情報が表示されます。この画面でメトリクスや、ログなどを確認することができます。   インフラストラクチャホストUI | New Relic Documentation For New Relic infrastructure monitoring, use the Hosts UI page to view the most important metrics from across your serve... docs.newrelic.com   インベントリUI この画面では、Infrastructureエージェントがインストールされたホスト内のOSやアプリケーションの構成要素、インストールされているパッケージなどを、ホストにログインせずに確認できます。構成情報はホストから送信される最新データが即座にUIに反映されます。ホストがシャットダウンしたりエージェントが停止してNew Relicへのデータ送信ができなくなった場合でも、最後に報告されたインベントリ情報は最大24時間UIに保持されます。 利用シーンとしては以下の例が挙げられます。 ・セキュリティ対策:脆弱性対応後、古いパッケージが残っていないか ・バージョン比較   :本番環境とテスト環境のパッケージや設定ファイルの差異を比較 ・構成管理            :ミドルウェアやサービスのインストール状況を把握   インフラストラクチャ インベントリ UI ページ | New Relic Documentation Use the New Relic infrastructure inventory page to monitor package and kernel versions across your entire architecture. docs.newrelic.com   イベントUI システムやホストの重要なアクティビティをリアルタイムで確認できます。インフラストラクチャーの変更やアクティビティを時系列で表示し、特定の時間に発生したイベントを一元管理できます。ホストごとにイベントを検索できますが、 画面右上で指定した時間範囲内のイベントのみが表示 されます。広範囲を調べたい場合は、時間範囲を拡大する必要がありますのでご注意ください。 利用シーンとしては以下の例が挙げられます。 ・障害の原因特定               :スパイクや異常値が発生した時間帯に、どんなイベントがあったかを確認 ・変更履歴の監査               :インベントリ(Kernel、Metadata、Packages、Services、Sessions)の変更履歴を記録 ・ユーザーセッションの監視:ユーザーの接続・切断イベントを記録し、セキュリティや運用状況を把握 ・構成変更の影響分析         :設定変更やパッケージ更新などのイベントと、CPUやメモリなどのメトリクスを比較     インフラストラクチャ イベント UI ページ | New Relic Documentation The New Relic infrastructure events UI page gives you a unified timeline of important changes and events in your archite... docs.newrelic.com   Infrastructure 機能を使いこなすために 大規模かつ動的に変化する環境を監視するために、すべてのホスト(DB・Webサーバーなど)へエージェントをインストールし、単一のダッシュボードでデータを一元的に確認することで、チーム間で情報を共有しやすくなります。また、アラート条件の設定やクラウドインテグレーションの有効化といったベストプラクティスにより、オブザーバビリティを迅速に向上させることができます。継続的に改善を繰り返すことで、障害発生時の原因特定や復旧までの時間(MTTR)を大幅に短縮し、クラウド・オンプレミスに関わらず、運用データを統合的に管理できるよう、以下指針がまとめられています。 項目 概要 1. Infrastructureエージェントを環境全体にインストール すべてのホスト(DB、Webサーバーなど)にエージェントを導入し、タグやカスタム属性で分類。 2. EC2インテグレーションを設定 AWSタグとメタデータを自動インポートし、タグでフィルタリングやアラート設定を実現。新しいインスタンスも自動追加。 3. インテグレーションを有効化 AWSサービスやホスト上のアプリを監視。事前設定されたダッシュボードを利用可能。 4. ホストグループビューの作成 エンティティフィルターでグループ化し、稼働ステータスを色分け表示し、問題箇所を迅速に特定。 5. アラート条件を作成 タグベースで条件を作成し、新しいホストにも自動適用。 6. InfrastructureデータをAPMデータと統合 Application Performance Monitoring(APM)とInfrastructureを並べて表示し、根本原因を迅速に特定。サービスマップでアプリとインフラの関連性を可視化。 7. メトリクスとイベントを活用 カスタムダッシュボードを作成。チームで共有し、インフラとアプリの健全性を一元管理。 8. エージェントを定期的に更新 新機能や改善を活用するため、最新バージョンへの更新を推奨。   Infrastructureモニタリングのベストプラクティスガイド | New Relic Documentation Best practices to make dynamic infrastructure and server monitoring even easier with New Relic. docs.newrelic.com   さいごに この記事では、New Relic Infrastructure の主要な機能について解説しました。New Relic は多機能なプラットフォームであり、インフラ監視ひとつをとっても観測できるデータは多いです。そのため、まだ取り上げきれていない機能や、より深く掘り下げられるトピックも数多く存在します。今後は、実際の運用シナリオで役立つ設定例や、ダッシュボード活用方法、アラートチューニング、クラウドサービスとの統合など、実践的な情報も解説していきます。引き続き、みなさまの環境運用に少しでも貢献できれば幸いです。 SCSKはNew Relicのライセンス販売だけではなく、導入から導入後のサポートまで伴走的に導入支援を実施しています。くわしくは以下をご参照のほどよろしくお願いいたします。
アバター
本記事は 新人ブログマラソン2025 1/22付の記事です。 みなさん、初めまして。2025年度に入社した新人の杉山です。 AWS歴はまだまだ浅い私ですが、資格勉強やハンズオン、そして実際の案件対応を通じて、日々学習に励んでいます! 今後も、学んだことや気づいたことなどをブログで発信していきますので、AWS初心者の奮闘記にぜひお付き合いください! 今回の記事では、私が実際に体験したAmazon SNS(Simple Notification Service)のサブスクリプションが、誤って削除されてしまう原因と、その対処法についてご紹介します。 Amazon SNSを今後使用される方々の参考になれば幸いです!! はじめに 事の発端は2025年11月。とある案件で Amazon CloudWatch + Amazon SNS によるアラーム通知を構築することになり、検証環境で動作確認を行っていました。 しかし、ある日を境に 通知メールが一切届かなくなる という事態が発生!! 不審に思い、コンソールでサブスクリプションの状態を確認してみたところ、なんと、IDが” 削除済み ”となっていました。 「なぜ削除されたのか?」と原因を調べた結果、あるリンクを誤ってクリックしてしまったことが原因 であることが判明したのです。 そのリンクとは…? 通知が消える悲劇の原因 Amazon SNSでは、サブスクリプション登録時や通知メールに “unsubscribe(登録解除)リンク” が自動的に挿入されています。 これはAWSの仕様で、ユーザーがいつでも解除できるようにするための仕組みです。 このリンクをクリックすると、以下のような画面に遷移し、サブスクリプションが登録解除されます。   問題は、このリンクを 誤ってクリック してしまうこと。 クリックすると、SNSトピックからサブスクリプションが削除され、通知メールが届かなくなります。 さらに、確認画面や警告はほとんどなく、ワンクリックで解除されるため、意図せずに登録解除してしまうケースも多いのです。 では、意図しない登録解除を避けるためにどうやってこの解除リンクを無効化するのか? 次の章では、解除リンクを無効化するための具体的な方法を詳しく解説します! unsubscribeリンクを無効化する方法 設定は 「SNSサブスクリプションの作成」 、 「”Confirm subscription”のリンク登録」 の2ステップで行います。 SNSサブスクリプションの作成 対象のトピック画面で「サブスクリプションの作成」をクリックします。 詳細を入力した状態で「サブスクリプションの作成」をクリックします。   作成が完了すると、IDが”保留中の確認”のサブスクリプションが表示されます。   ”Confirm subscription”のリンク登録 サブスクリプションの作成後、「AWS Notification – Subscription Confirmation」というメールが届きます。 ”Confirm subscription”のリンクをクリックしないでください!! 代わりに”Confirm subscription”のリンクを 右クリックしてコピー します。 次にコンソールに戻り、「サブスクリプションの確認」をクリックします。 先ほどコピーしたURLをペーストし、「サブスクリプションの確認」をクリックします。     URLは以下の形式です。 https://sns.<リージョン>.amazonaws.com/confirmation.html?TopicArn=arn:aws:sns:<リージョン>:<AWSアカウントID>:<トピック名>&Token=<トークン値>&Endpoint=<メールアドレス> Outlook上でURLをコピーする際、Microsoft Defender for Office 365の「Safe Links」機能が有効になっている場合は、リンクがセキュリティ保護のために書き換えられるため、元のURLにデコードする必要があります。 すると、サブスクリプションのIDが変更され、ステータスが” 確認済み ”になります。 では、本当にunsubscribeリンクが無効化されたのか?次の章で動作確認を行います! 動作確認 再作成したサブスクリプションに登録したメールアドレス宛に、テストメッセージを送信してみましょう。 コンソール上で、画面右上にある 「メッセージの発行」 をクリックします。 次に、「エンドポイントに送信するメッセージ本文」 に任意の文字列を入力し、画面右下の 「メッセージの発行」 をクリックします。   すると、テスト用のメールが届きます。メール本文には、従来通り unsubscribeリンク が含まれています。 ここで、そのリンクをクリックしてみると…   サブスクリプションが解除されないことを確認できました! さいごに 今回の記事では、Amazon SNSの通知メールに含まれる「unsubscribeリンク」が誤クリックによって通知停止を引き起こす問題と、その対策について解説しました。 この方法を実施することで、誤クリックによる通知停止のリスクを大幅に減らすことが可能です。 今回の方法をぜひ試して、「通知が消える悲劇」からシステムを守りましょう! 最後まで読んでいただきありがとうございました! 参考サイト SNSトピックのunsubscribeの無効化設定 - Qiita はじめに 今回業務の中でSNSのサブスクリプションを誤って解除してしまうのを無効化したいという要件があったため、調べた中で作り直しが必要と記載のある記事が多く、作り直さなくても設定できたので、メモとして残そうと思います。 unsubscri... qiita.com
アバター
AWS を学んでいて、思わず 「 Gatewayと名の付くサービス多くないか? 」 と感じたことがありました。 Direct Connect Gateway、Transit Gateway、API Gateway、NAT Gateway、ゲートウェイ型 VPC エンドポイントなど。 AWS SAP(Solutions Architect Professional)の勉強中に混乱したのが正直なところです。 AWSのGateway系サービスを主題に、Azureとの比較を通して役割を整理しました。 AWSを主に扱っている方には、Azureと比べることでGatewayと名の付くサービスの多さを感じてもらいたいです。 Gatewayとは 一般的なIT用語としてのゲートウェイは、異なるプロトコル間の通信を中継する仕組みを指します。 ネットサーフィンしてると、ゲートウェイを解説するSCSKのサイトを見つけました。 ゲートウェイとは|IT用語辞典|SCSK IT Platform Navigator ゲートウェイ(Gateway)とは、英語で「出入口」を意味する言葉。異なる通信プロトコルを使用するネットワーク間を中継する機器または仕組みを指す。 www.scsk.jp 一方、AWSにおいてのGatewayは、必ずしもプロトコル変換を行うわけではなく、 「ネットワーク境界の入口」くらいの意味で使われていそうです。(※個人の意見) 比較表 AWSのサービスとAzureを比較しました。 一対一対応ではないですが、概念的に近いサービスをまとめてます。 AWSサービス AWSサービスの概要 Azureサービス Direct Connect オンプレ環境からAWSへ専用線ネットワークを提供 ExpressRoute Direct Connect Gateway Direct Connect接続を複数のリージョンのVPCで共有 ExpressRoute Global Reach Transit Gateway  複数のVPC間・オンプレ間のルーティングを集中管理 Virtual WAN Site-to-Site VPN  オンプレ環境や他クラウドのネットワークとAWSの通信をIPSecを使用しセキュアに通信 Azure VPN Gateway ゲートウェイ型 VPC エンドポイント S3とDynamoDBなどが対象のエンドポイント Azure Vnet Service Endpoint インターフェイス型 VPC エンドポイント 多くのサービスが対象でプライベート接続を行う際のエンドポイント Private Endpoint API Gateway REST/HTTP/WebSocket API のフロントドア API Management AWS NAT Gateway プライベートサブネット内のインスタンスからインターネットへの接続 Azure Nat Gateway   おまけ よくでる構成図 AWS SAPでよく出てくるDirect ConnectとTransit Gatewayの簡易的な構成図を作ってみました。 組み合わせで出題されてたイメージで、印象に残ってます。               AzureもNAT Gatwayが必須に Azure Nat Gateway について。 AzureではこれまでNAT Gatewayを作成しなくてもインターネット通信が可能であり、便利だなと思ってました。 自動で azure-default-snat なるものが作成されていたようです。 詳しくはこちら 【Azure】Azure NAT Gateway のすゝめ 昨今はAzure仮想マシンから外部SaaSのデータ連携をするケースが多くあり、SaaS側のセキュリティ対策として特定のグローバルIPアドレスのみ通信許可することがあります。通常、仮想マシンに用意されているグローバルIPアドレスはAzure側で作成されたグローバルIPアドレスであり再起動やAzure側の任意のタイミングで意図せず変更されてしまうことから、AzureNATGatewayを利用することでグローバルIPアドレスを固定可能となります。 blog.usize-tech.com 2024.01.26 しかし廃止となり、2025年10月以降にはAzure NatGateway等の明示的なアウトバウンド構成が推奨されたようです。 https://azure.microsoft.com/ja-jp/updates?id=default-internet-outbound-access-for-simplified-node-communication-batch-pools-without-public-ip-addresses-will-be-retired-on AWSの勉強してて驚いたAzureニュースでした。   参考文献 AWS と Azure のネットワーク オプションの比較 - Azure Architecture Center Azure と AWS のネットワーク オプションを比較します。 クラウド仮想ネットワーク、クロスプレミス接続、DNS 管理などについて比較します。 learn.microsoft.com AWS サービスや Azure サービスと Google Cloud を比較する  |  Get started  |  Google Cloud Documentation docs.cloud.google.com
アバター
ブログリレー 前回の記事 では、バックエンドのあれこれを齋藤さんが記事にしてくれましたが、本日はフロントエンド編ということで、AWSパラメータシート自動生成ツールのGUIをどのような技術を使用して実現したかをご紹介できればと思います! フロントエンド概要 フロントエンドは下記のような流れでバックエンド(パラメーターシート出力)に処理が引き継がれます。 上記処理のうち、本記事のフロントエンドは「GUI」と「GUIを提供するサーバー」部分を担当しています。 使用した技術 フロントエンド実現のために使用した主な技術とその役割は下記の通りです。 技術 役割 JavaScript クライアント側、サーバーサイド側のロジックを記述するために使用した言語 Node.js  JavaScriptの実行環境であり、アプリケーション全体の実行基盤 Express.js WebサーバーおよびREST APIなどのサーバーサイド側の機能を提供してくれるWebフレームワーク HTML5 + CSS3 画面構造とデザイン フロントエンドは、クライアント側、サーバーサイド側も一貫してJavaScriptを使用して実装しています。 サーバーサイド側の開発として、Ruby + Rails や Python + Django といった組み合わせもある中で、JavaScript + Node.js を使用した理由としては、以下の通りです。 案件で JavaScript を触る機会があり慣れている よく耳にする組み合わせでなんかかっこいい しかし、よくよく調べるとよく耳にする理由がありました。それについては以降で説明していきたいと思います。 Node.js Node.jsとは? そもそもNode.jsとは何者かというと、 JavaScriptをサーバー上で実行するための開発環境 です。 元々、JavaScriptという言語は他の言語と異なり、ブラウザ上で動作する言語であり、ローカル(OS上)環境では動作させることができない言語でした。 よって、ブラウザ上でしか動作しない JavaScript ではローカル環境にあるファイルを読みにいくことができないという問題がありました。 その制限を取っ払ってくれたのが、Node.jsという訳です。 Node.jsの登場のおかげで、 クライアント・サーバーサイドの両者を同一の言語で開発 することができるようになりました。 Node.jsの特徴 Node.jsの特徴としては、主に以下の3つが挙げられます。 1. ノンブロッキングI/O処理 前のタスクが完了していない状態でも次のタスクを開始でき、 非同期での処理を実現 している。 そのため、大量のアクセスがあっても対応が可能である。 2. シングルスレッドによる処理 プログラムを実行する際に、1つずつ処理を行う 。 一般的にシングルスレッドだと大量のアクセスがあった場合に制御することが難しくなるが、ノンブロッキングI/O処理によって、多くのアクセスがあってもリアルタイムでレスポンスが可能になる。 3. 豊富なライブラリ サーバーサイドアプリケーション、デスクトップアプリケーション、コマンドラインツールなどの幅広い用途で使用されるため、非常に多くのライブラリがある。 npmというパッケージ管理ツールを使用することにより、膨大なライブラリを簡単にインストール・管理することができる。 これらの特徴から、Node.js + JavaScript は 大量の同時処理をさばけ、なおかつ様々な目的で使用できる汎用的な技術基盤 として人気があるという訳です。 Express.js ここまでで、Node.jsを使用した理由や用途は理解できたかと思いますが、あくまでもNode.jsはサーバーサイド側でJavaScriptを実行できるようにした実行環境であり、ただの基盤です。 フロントエンドを作成する上では、基盤の上に実際の機能を作成してあげる必要があります。 その実際の機能は一から作る必要はなく、既に用意されたひな形から作成することができます。 それにあたるのが、Express.jsです。 Express.jsとは? Express.jsとは、 Node.jsのための軽量で柔軟なWebアプリケーションフレームワーク です。 フレームワークとはひな形とも言い換えることができ、様々なひな形が用意されています。 例えば、機能開発が簡単に実装できる以下のようなひな形が用意されています。 ルーティング機能 ユーザーが特定のURLにアクセスした際に、 どのようなレスポンス(応答)を返すかを定義 することができる。 例えば、ユーザが / にアクセスしたら、トップページを表示する、/download/file.xlsx にアクセスしたら、ファイルをダウンロードするなどがある。 REST API機能 フロントエンド(ブラウザ)からのリクエストを受け取り、 Web上でデータをやりとりするための操作を定義 することができる。その操作には以下のものがあり、これらを定義することができる。 HTTPメソッド 操作 GET リソースを取得する POST リソースを作成する PUT 指定したIDのリソースを更新する DELETE 指定したIDのリソースを削除する 静的ファイルの配信 静的ファイル(HTML、CSS、JavaScript、画像など)をユーザーに配信することができる。 また、HTTPヘッダーによるキャッシュ制御も可能で、同じファイルへの再リクエスト時のレスポンス時間を短縮できる。 どこに使用したの? では、フロントエンド実現にあたってどこに活用したのかというと下記の通りです。 機能 使用箇所 ルーティング機能 トップページの表示 ファイルのダウンロード REST API機能 AWSリソース一覧取得 Excel生成リクエスト(バックエンドへのトリガー) 静的ファイルの配信 HTML/CSS/JavaScriptファイルの配信 苦労した点 リソースごとに異なるJSONファイル フロントエンドでは、AWSのリソースの詳細情報を表示するために、AWS CLIコマンドを実行し、リソースの一覧を取得してきています。その一覧情報の構造が、リソースによって異なり、リソースごとに取得する情報を定義してあげることが苦労しました。 例: EC2インスタンスの場合: {   “Reservations”: [     {       “Instances”: [         { “InstanceId”: “i-xxxxx”, “State”: {…} }       ]     }   ] } S3バケットの場合: {   “Buckets”: [     { “Name”: “my-bucket”, “CreationDate”: “…” }   ] } このように、同じ「リソース一覧を取得する」という処理でも、項目名やJSONファイルのネスト構造の深さが異なります。そのため、すべてのリソースに対して、どのキーからデータを取り出すか、IDとして何を使うかを個別に定義する必要がありました。 一方で、共通化できる部分は共通化し、できるだけ同様のコードで詳細情報を取得できるようにしてあげました。やはりコードの最適化には、AIを使用してあげるのが効率よかったです。 特殊なリソースの取得フロー 一般的なリソースの場合、取得フローは下記のようになります。 サービス選択 ⇒ リソース一覧から出力したいリソースを選択 ⇒ プレビュー画面表示 ⇒ Excel 出力 しかし、サービスの中には一発で一覧を取得できないサービスがあります。 例えば、ELBのターゲットグループの場合、ELBに紐づくターゲットグループを取得する必要があるため、まず最初にELBの一覧を取得し、その後にターゲットグループを取得する必要があります。よって、一覧の取得フローとしては下記のようになります。 サービス選択(ELBターゲットグループ)⇒ リソース一覧から出力したいリソースを選択(ELB)⇒ リソース一覧から出力したいリソースを選択(ターゲットグループ)⇒ プレビュー画面表示 ⇒ Excel 出力 このようにサービスによっては、中間ステップが存在するものもあるため、コードを共通化することができず、苦労しました。 まとめ 本記事では、AWSパラメータシート自動生成ツールのGUIであるフロントエンドで使用した技術について簡単に紹介しました。 実は、この取り組みはフロントエンドのアプリケーションを作成するつもりはなく、バックエンドのJSONファイルをパラメータシートに変換するアプリケーションのみを構築予定でした。しかし、使用するユーザー目線で考えると、やはりGUIがほしいということになり、開発に取り組みました。 普段はインフラ構築を担当する部署のため、初めて触れる技術が多く、開発には3~4か月ほどかかってしまいましたが、この取り組みを経てアプリケーションの知識をつけることができ、フルスタックエンジニアへと一歩近づいた気がします。 皆さんもぜひ、息抜きもかねて普段とは異なる分野の技術に触れてみるのはいかがでしょうか? というわけで、私の投稿は以上です! 次回は、AWSパラメータシート自動生成ツールを使ってみたということで、藪内さんにバトンタッチしますので、ぜひそちらも閲覧いただければと思います。
アバター
こんにちは、ひるたんぬです。 電子レンジ、生活の中に当たり前のように溶け込んでいる存在ですよね。 でも、「レンジ」とはどこからやってきた要素でしょうか? 発明元のアメリカ(英語)でも”microwave (oven)”であり、「レンジ」の要素は見当たりません。。 歴史を辿るとヒントがありました。 電子レンジの加熱原理は、アメリカのレイセオン社によって、レーダー 装置 の実験中にお 菓子 がレーダーのマイクロ波によって 溶 けていることに気が付き、これにヒントを得て、 マグネトロンを使った調理器「レーダーレンジ」として製品化されました。 引用:一般社団法人 日本電機工業会「 電子レンジの歴史 」 「電子レンジ」という言葉もここから生まれたのかも知れないですね¹。 ¹ 「レンジ」の由来やその意味について、明確な文献を見つけることはできませんでした。日本ではその前から「ガスレンジ(ガスコンロ)」という名称が広がっており、電気的に加熱料理できる調理器具として「電子レンジ」とつけられたのではないか、という説もあるようです。 さて、今回はCloudFrontの機能の一つ、Lambda@Edgeにおけるログについて少し紐解いていきます。 やりたいこと Amazon CloudFrontを使っており、その中でLambda@Edgeを使用しています。 ここで、Lambda@Edgeから出力されるログについて、予めログの有効期限を設定しておきたい場合を考えます。 既定では… AWSの公式ドキュメントなどにも記載がありますが、Lambda@Edgeでは関数が実行された場所に最も近いAWSリージョンにロググループが自動で作成され、ログイベントが保管されます。 チュートリアル: 基本的な Lambda@Edge 関数 (コンソール) を作成する - Amazon CloudFront Amazon CloudFront で実行される基本的な Lambda@Edge 関数をコンソールを使用して作成する方法について説明します。 docs.aws.amazon.com エッジ関数のログ - Amazon CloudFront Amazon CloudWatch Logs を使用して、Lambda@Edge 関数と CloudFront Functions の両方のエッジ関数のログを取得してください。 docs.aws.amazon.com また、自動で作成されるロググループは有効期限が未設定(失効しない)のため、明示的に削除しないとログが残りつづけます。 コンプライアンスや規約上、ログはすべて保管する必要がある場合などはこの設定でも問題はありませんが、ログイベントの保存にも料金は発生します。 2026年1月現在では、東京リージョンにおいて1GBあたり$0.033の保存費用が発生します。 課題点 以上のことから、Lambda@Edgeでログを保管する場合、デフォルトの設定では無期限でログが残り続け、主に費用面での負担が増大しかねない点をご説明しました。 では、予めロググループを作成し、ロググループに対して有効期限を設定したいのですが…それには Lambda@Edgeがどこで実行されうるのか(どこのAWSリージョンにログが出力されうるのか) 、を知る必要があります。 Lambda@Edgeが実行される場所 これを理解するためには、Amazon CloudFrontについても知る必要があります。 CloudFrontは「エッジロケーション(≠ リージョン)」と呼ばれる場所でキャッシュサーバーとして機能しています。 エッジロケーションにてキャッシュヒットしなかった場合は「リージョン別エッジキャッシュ(≠ リージョン)」にアクセスし、それでもヒットしなかった場合はオリジンからコンテンツを取得します。 引用: AWS Black Belt Online Seminar – Amazon CloudFront 基礎編(P. 15) そして、Lambda@Edgeは、上記の「リージョン別エッジキャッシュ」にデプロイされています。 CloudFront Functionsについては「エッジロケーション」にデプロイされています。 そのため、CloudFront Functionsの方が実行時間が早い一方、キャッシュ容量が少ないためコード容量の制限が厳しいものだと分かります。 パソコンのCPUにおけるL1・L2キャッシュとイメージすると少し分かりやすいでしょうか…? 引用: AWS Black Belt Online Seminar – Amazon CloudFront (CloudFront Functions / Lambda@Edge 編) (P. 17) では、その「リージョン別エッジキャッシュ」はどこにあるのでしょうか? 2026年1月現在では13個所に設定されています。 米国東部 (バージニア北部) – us-east-1 米国東部 (オハイオ) – us-east-2 米国西部 (北カリフォルニア) – us-west-1 米国西部 (オレゴン) – us-west-2 アジアパシフィック (ムンバイ) – ap-south-1 アジアパシフィック (ソウル) – ap-northeast-2 アジアパシフィック (シンガポール) – ap-southeast-1 アジアパシフィック (シドニー) – ap-southeast-2 アジアパシフィック (東京) – ap-northeast-1 欧州 (フランクフルト) – eu-central-1 欧州 (アイルランド) – eu-west-1 欧州 (ロンドン) – eu-west-2 南米 (サンパウロ) – sa-east-1 明示的な一覧は公式サイトからは見ることができなかったので、以下の資料から読み取りました。 (すべてAWSが提供する資料です) Amazon CloudFront 用のリージョン別エッジキャッシュを発表 サービスアップデート Edge Networking Services 編 └ P.8 Amazon CloudFront が AWS の欧州 (アイルランド) リージョンで新しいリージョン別エッジキャッシュを開始 また、CloudFrontのユーザーマニュアルからもLambda@Edgeのサービスにリンクされたロールの使用がサポートされているリージョンにて、同様のものを挙げています。 デベロッパーガイド – Amazon CloudFront └ P. 935(「Lambda@Edge サービスリンクロールでサポートされている AWS リージョン 」)にリージョン一覧の記載があります。 以上のことから、すべてのAWSリージョンでリージョン別エッジキャッシュが提供されているわけではないことが分かりました。(例えば、大阪リージョンでは提供されていないことが分かりやすいかと思います。) 結論 以上のことから、予めロググループを作成し、ロググループに対して有効期限を設定したい場合は、上述しました13個のリージョンに対して、所定の命名規則や設定に沿ったロググループを作成すれば良い、ということになります。 Lambda@Edgeにおけるロググループ名の規則については、以下をご参照ください。 エッジ関数のログ - Amazon CloudFront Amazon CloudWatch Logs を使用して、Lambda@Edge 関数と CloudFront Functions の両方のエッジ関数のログを取得してください。 docs.aws.amazon.com なお、今後リージョン別エッジキャッシュが拡大することも考えられますので、先んじて他リージョン(大阪リージョンなど)でロググループを作成しておく、というのも一手かと思われます。 ロググループの作成自体にはAWSの利用料金は発生しません。 一方、日本国内にアクセスを限定しており、海外からのアクセスは遮断している場合などについては、更に事前に作成するリージョンを絞ってよい(東京リージョンのみ、など…)かと思います。   終わりに 今回は、CloudFrontにLambda@Edgeを組み合わせて使用する場合において、どのリージョンにロググループが必要なのか(≒ Lambda@Edgeの実行に関連するAWSリージョンがどこなのか)調査しご紹介しました。 公式ドキュメントなどでも言及がほとんどされておらず情報収集に苦戦しましたが、参考になる方がいらっしゃいましたら幸いです。 余談ですが、「レンジでチン」という言葉は死語になってしまう日もくるのでしょうか。 テレビにおける「チャンネル回す」も死語になりつつあるようです… 最近の電子レンジは、「ピー」という音がなったり、おしゃれな音楽が流れるようです。「レンジでピー」…?
アバター
こんにちは、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 環境までつくれると良いですね。でも一度作ればあまり変更が入らない環境なので、そこまでやるか?とも思いますね。 本記事が皆様のお役に立てれば幸いです。
アバター