TECH PLAY

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

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

1141

SCSKの畑です。4回目の投稿です。 今回は、現在作成中の WEB アプリケーションにおいて、どのように排他制御を実装したのかについてまとめました。ただ、内容が長くなりそうなので、何回かに分けて記載していこうと思います。 排他制御が必要となる機能要件について 初回のエントリ に記載通り、現在作成中の WEB アプリケーションの主機能はデータベース/ DWH 上のテーブルデータのメンテナンスとなっています。どのようにテーブルデータのメンテナンスを行いたいか(=どのような機能要件か)というのは案件ごとに差異はありますが、そのような機能を実装する上で何らかの排他制御の仕組みを考慮する必要がある、というのはイメージできるところだと思います。 例えば排他制御の仕組みを設けなかった場合、同じテーブルのデータを複数ユーザが同時に編集するようなケースにおいて、編集されたデータに不整合が生じる可能性が高くなります。(ユーザAとユーザBが同時に更新を行った場合ユーザAが行った更新が反映されない、など) 今回の投稿では、以下のような機能要件を持つ案件事例をベースに記載していきたいと思います。 テーブルデータ編集時に、テーブル単位での排他制御(ロック)を行う ※いわゆる表レベルロック あるユーザがデータ編集中のテーブルを、他ユーザが同時に編集することはできない。 外部キーを持つテーブルのデータ編集時には、外部キーの参照先テーブルについても合わせて排他制御(ロック)を行う 例えばテーブル A の外部キー参照先が テーブル B だった場合、テーブル A の編集を開始した時点(テーブル A をロックした時点)で テーブル B もロックされ、他ユーザが編集できなくなる。 逆に テーブル B がすでにロック中だった場合、テーブル A は編集できない。 総じて、(複数テーブル間も考慮した)データ整合性を重視していますが、本案件で注力したのが正に「メンテナンス時のデータ整合性の担保」だったこともあり、方向性通りの内容ではあります。逆に表レベルロックという仕組みの都合上ユーザの利便性は下がりますが、想定ユーザ数やデータメンテナンスの頻度などをお客さんと会話の上、この機能要件で問題ないという整理になりました。 なお、行レベルロック(ユーザが編集中の行のみ排他制御する)という方式もありますが、ユーザの利便性や同時実行性について向上する反面、設計・実装の難易度が上がります。また、メンテナンス対象のデータベース / DWH の機能に実現可否が左右されることもあります。 アーキテクチャ概要、及び排他制御に使用したサービス / コンポーネント そろそろ毎度のことになりつつありますが、 初回のエントリ からアーキテクチャ図を再掲します。 排他制御関連の機能は、以下のサービス / コンポーネントで実現しています。本投稿では主に DynamoDB と Lambda の部分について記載したいと思います。少しだけ Amplify のスキーマ定義が入ってきますが、排他制御という観点だとこの2つがメインとなります。 Amazon DynamoDB テーブルのステータス(編集状態)管理 AWS Lambda 排他制御を考慮したテーブルのステータス更新ロジックの実装 AWS AppSync(AWS Amplify) 上記 Lambda をアプリケーション上から実行するためのスキーマ定義 アプリケーション(Nuxt.js) テーブルのステータスに応じた画面制御 DynamoDB における設計・実装 ということで上記の通り、テーブルのステータス(編集状態)を管理するために DynamoDB を使用しています。 対象テーブルのステータス(編集状態)を永続化して管理する必要があること ステータスは複数のユーザから参照・更新できること 排他制御(ロック)して複数テーブルのステータスを同時に更新できること の3点より、何らかのデータベースを使用するのが妥当と判断しました。最も「判断した」と記載できるほどではなく、当たり前によくある話だとは思いますが、、ただこの3点目については重要なので補足します。 テーブル単位の排他制御(ロック)である以上、先述したような「同一のテーブルを複数ユーザが同時に編集する」ケース自体は考慮する必要がありません。編集中のテーブルについては他のユーザが編集できないようにアプリケーション側で実装すれば良いからです。ただ「同一のテーブルを複数ユーザが同時に編集しようとする(=同一テーブルのステータスを複数ユーザが同時に更新する)」ケースについては考慮しておかないと、タイミング次第で複数ユーザが同時に同一テーブルの編集を更新できる状態となってしまう可能性があります。よって、データベース側で「排他制御(ロック)」の仕組みが必要となります。 また、機能要件のセクションで記載した通り、外部キーの存在するテーブルを編集する場合は、対象だけでなく外部参照先のテーブルについても同時にステータスを更新できる必要があります。つまり、データベース側で複数の更新処理(≒SQL文の実行)を単一の処理として扱える「トランザクション」の仕組みについても必要となります。 データベースと名前の付いた製品なりサービスでは基本的にこれらの機能は備えており、当然ながら DynamoDB でも可能です。どのように実現したのかについて、以下に記載していきます。 DynamoDB における排他制御(ロック) さて、データベースによってどのような方式で排他制御(ロック)ができるかどうかは異なります。悲観ロック・楽観ロックの2種類がありますが、DynamoDB で取得できるロックは後者の楽観ロックとなります。具体的には以下ドキュメントの「 条件付き書き込み 」を使用します。 DynamoDB での項目と属性の操作 - Amazon DynamoDB Amazon DynamoDB の基本的な構成要素は、テーブル、項目、および属性で始まります。これらの要素と基本的な CRUD オペレーションを使用して、強力でスケーラブルなアプリケーションの構築を開始する方法について説明します。 docs.aws.amazon.com また、ステータスの更新に条件付き書き込みを使用する上でステータスの遷移条件を定めておく必要があります。例えば、ステータスを通常状態から編集状態に遷移(更新)できれば、正常なステータス遷移と見なし更新を確定する、という流れです。(もちろんアプリケーション自体の設計としても定めておく必要がありますが・・) DynamoDB におけるトランザクション 上記の通り、複数の更新処理(≒SQL文の実行)を単一の処理として行うために、以下ドキュメントの「 TransactWriteItems API 」を使用します。 Amazon DynamoDB Transactions: 仕組み - Amazon DynamoDB API オペレーション、キャパシティ管理、エラー処理、ベストプラクティス、トランザクションオペレーションの使用に関する詳細など、DynamoDB トランザクションの仕組みについて説明します。 docs.aws.amazon.com 設計・実装例 テーブルのステータス(編集状態)を管理するための、amplify を使用したスキーマ定義例です。各項目の意味はそれぞれ以下の通りです。 name:テーブル名 status:テーブルのステータス editor:テーブルが編集状態における、編集しているユーザ名(通常状態の場合は null) locked_by:テーブルがロック状態における、外部キー参照元のテーブル名(通常状態の場合は null) enum TableStatus { normal editing locked } type TableInfo @model @auth(rules: [ {allow: public, provider: apiKey}, {allow: private, provider: iam}, ]) { name: String! @primaryKey status: TableStatus! editor: String locked_by: String } ステータスについては、enum で特定の値のみが入力されるように定義しています。実案件では他の要件もあったことからもう少しステータスの種類が多かったのですが、説明のために簡略化しています。 normal:通常状態 editing:編集状態 locked:ロック状態(=外部キー参照元のテーブルが編集中) また、ステータスの遷移については、以下の通り通常状態をベースに編集状態またはロック状態に遷移するような定義とします。(ちゃんとした状態遷移図でないのはご了承ください・・)  Lambda における設計・実装 DynamoDB のテーブルに対する汎用オペレーション(get, list, create など)については Amplify が自動生成してくれる graphql の query / mutation を使用していますが、残念ながら「条件付き書き込み」や「TransactWriteItems」を実現してくれるようなスキーマ定義の方法はありません。よって、テーブルステータスの更新については、Lambda で実装したものを AppSync 経由で実行するような棲み分けとしています。 Amplify(schema.graphql)における mutation のスキーマ定義例 type を input として使い回せないのが唯一イマイチな仕様だなと思っているのですが、それ以外は特に違和感なく使えています。機能・記法としてこういうものが欲しいみたいな話はまた別にありますが。。 input UpdateTableStatusWithLockInput {     table_name: String!     current_status: TableStatus!     target_status: TableStatus!     fk_table_name: [String!]     fk_current_status: TableStatus     fk_target_status: TableStatus     editor: String } type FKResultTableStatus { table_name: String! current_status: TableStatus! } type ResultTableStatus {     lock_result: Boolean!     current_status: TableStatus     fk_current_status: [FKResultTableStatus!] } type Mutation { UpdateTableStatusWithLock(input: UpdateTableStatusWithLockInput!): ResultTableStatus @function(name: "<Lambda関数名>") @aws_api_key @aws_iam } 入力における各項目の意味は以下の通りです。外部キー(FK)参照先のテーブルのステータス更新に対応するため「fk_」から始まる引数を設けています。外部キーの有無はテーブルによって異なるため、それらの引数は必須とはしていません。また、外部キーが複数あるケースを想定して、fk_table_name については list で指定するようにしています。 table_name:ステータス更新対象のテーブル名 current_status:更新前の想定ステータス target_status:更新後の想定ステータス fk_table_name:ステータス更新対象の FK 参照先テーブル名 fk_current_status:FK 参照先テーブルの更新前の想定ステータス fk_target_status:FK 参照先テーブルの更新後の想定ステータス editor:ステータス更新対象テーブルの編集ユーザ名 locked_by:FK 参照先テーブルをロックしている、参照元のテーブル名 (fk_)current_status 及び (fk_)target_status を DynamoDB の条件付き書き込みで使用します。具体的には、ステータス更新試行時に「 対象テーブルのステータスが current_status と同一であれば、target_status にステータスを更新する 」という動作となります。よって、複数ユーザが同時にステータスを更新しようとしても、ステータス更新が成功するのは(=対象テーブルのロックを取得することができるのは)いずれか単一のユーザのみ、という結果となります。 ちなみに上記のような結果が保証されるためには、データベースのトランザクション分離レベルについても考慮する必要があります。今回の要件に基づくユースケースにおいて、 DynamoDB の場合は全て SERIALIZABLE のため考慮不要でした。このへんの話も始めると収拾がつかなくなるので、また機会があれば。 出力についても一応記載しておきます。このあたりはアプリケーション側でどのような使い方を想定するかにもよると思いますが。。 lock_result:ステータス更新の成否 current_status:ステータス更新試行後の対象テーブルのステータス fk_current_status:ステータス更新試行後の FK 参照先テーブルのステータス Lambda 関数の実装例 次に、上記定義に合わせた Lambda 関数の実装例です。ランタイムは Python です。 入出力ともほぼ上記スキーマ定義通りに実装すれば良いのが直感的で良いですね。反面、「条件付き書き込み」や「TransactWriteItems」を使用する場合は boto3.resource が使用できず、 boto3.client を使用しないといけない関係で初期化部分のコードがイカつい見た目になってしまってますが。。(特に DynamoDB の型情報部分) boto3.resource については下記 URL の通り今後新機能の追加予定がないとのことです。DynamoDB では型情報が扱いやすかったのが良いところだったと思っているので、そのへんが逆に client 側に反映されるとありがたいですね。調べたところ、代替手段自体は他にもあるようでしたが・・ Resources - Boto3 1.35.92 documentation boto3.amazonaws.com import os import boto3 import traceback from datetime import datetime, timezone, timedelta from botocore.exceptions import ClientError client = boto3.client('dynamodb') def lambda_handler(event, context):   try:       # 引数、返り値の初期化       result_json = {}       result_json['lock_result'] = False       table_name = {'S': event['arguments']['input']['table_name']}       current_status = {'S': event['arguments']['input']['current_status']}       target_status = {'S': event['arguments']['input']['target_status']}       fk_table_name = [{'S': x} for x in event['arguments']['input']['fk_table_name']] if 'fk_table_name' in event['arguments']['input'] else []       fk_current_status = {'S': event['arguments']['input']['fk_current_status']} if 'fk_current_status' in event['arguments']['input'] else None       fk_target_status = {'S': event['arguments']['input']['fk_target_status']} if 'fk_target_status' in event['arguments']['input'] else None       editor = {'S': event['arguments']['input']['editor']} if 'editor' in event['arguments']['input'] else {'NULL': True}       locked_by = {'S': event['arguments']['input']['table_name']} if 'locked_by' in event['arguments']['input'] else {'NULL': True}       current_time = {'S': datetime.now(timezone.utc).isoformat(timespec='milliseconds').replace('+00:00','Z')}         # FK参照先テーブルロック解除時、editorを削除するように設定       if event['arguments']['input']['target_status'] == 'normal':           fk_editor = {'NULL': True}       else:           fk_editor = editor         # 対象テーブルの状態遷移       target_items = [{           'Update': {               'TableName' : <MASTER_TABLE_NAME>,               'Key' : {                   'name': table_name,               },               'UpdateExpression' : "SET #st = :upd_val1, updatedAt = :upd_val2, editor = :upd_val3",               'ConditionExpression' : "#st = :cond_val1",               'ExpressionAttributeNames' : {'#st' : 'status'},               'ExpressionAttributeValues' : {                   ':upd_val1': target_status,                   ':upd_val2': current_time,                   ':upd_val3': editor,                   ':cond_val1': current_status               }           }       }]       # 対象テーブルのFK参照先テーブルの状態遷移       for lock_table in fk_table_name:           target_items.append({               'Update': {                   'TableName': <MASTER_TABLE_NAME>,                   'Key': {                       'name': lock_table,                   },                   'UpdateExpression': "SET #st = :upd_val1, updatedAt = :upd_val2, editor = :upd_val3, locked_by = :upd_val4",                   'ConditionExpression': "#st = :cond_val1",                   'ExpressionAttributeNames': {'#st' : 'status'},                   'ExpressionAttributeValues': {                       ':upd_val1': fk_target_status,                       ':upd_val2': current_time,                       ':upd_val3': fk_editor,                       ':upd_val4': locked_by,                       ':cond_val1': fk_current_status                   }               }           })       # 条件付き書き込みの実施       update_response = client.transact_write_items(           TransactItems=target_items       )       # 返り値の設定       result_json['lock_result'] = True       result_json['current_status'] = event['arguments']['input']['target_status']       result_json['fk_current_status'] = []       for lock_table in fk_table_name:           result_json['fk_current_status'].append({'table_name': lock_table['S'], 'current_status': fk_target_status['S']})     # 条件付き書き込み失敗時の処理   except ClientError as e:       print(traceback.format_exc())       try:           # 返り値の設定           response = client.get_item(               TableName = <MASTER_TABLE_NAME>,               Key={'name': {'S': event['arguments']['input']['table_name']}}           )           result_json['current_status'] = response['Item']['status']['S']           result_json['fk_current_status'] = []           for lock_table in fk_table_name:               response = client.get_item(                   TableName = <MASTER_TABLE_NAME>,                   Key={'name': lock_table}               )               result_json['fk_current_status'].append({'table_name': lock_table['S'], 'current_status': response['Item']['status']['S']})         except Exception as e:           print(traceback.format_exc())     finally:       return result_json 実装例の内容について全て説明すると長くなってしまうので、要点のみ箇条書きでまとめます。 冒頭で、AppSync から渡される引数と、AppSync に返す返り値の初期化をしています。 TransactWriteItems により、単一のトランザクション内で実行したいクエリを target_items リストに格納しています。 外部キー(FK)のないテーブルは、TransactWriteItems で実行するクエリ数は1つです。 外部キー(FK)のあるテーブルは、外部キー参照先テーブルの個数分クエリ数が増加します。 各クエリはステータス更新を伴うため、全て条件付き書き込みを使用しています。 ‘ConditionExpression’ で条件付き書き込みの「条件」を指定しています。今回の条件は、ステータスが想定通りの値となっているかとなります。 ‘ExpressionAttributeNames’ は、DynamoDB スキーマ定義で使用している「status」という単語が予約語にあたるようで、そのまま使用するとエラーになってしまうため、クエリ内で一時的に別の単語に置き換える目的で使用しています。 https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html つまり、TransactWriteItems で実行される各クエリの内1つでも失敗した場合は、トランザクション内の他のクエリについても実行されず、実質的に対象テーブルのロックを取得できなかったことになります。 条件付き書き込みが失敗した場合(≒対象テーブルのロックを取得できなかった場合)は ClientError が発生するので、それを except 句で拾って例外処理を実装しています。 後は、この Lambda 関数を AppSync API 経由で実行するような画面を作れば、ユーザがアプリケーション上でテーブルのステータスを変更する際に排他制御御(ロック)が実現できるということになります。 まとめ 第二回はここまでで作成した仕組みをアプリケーション上からどのように使用しているかについて記載する予定です。最も、ステータス遷移時における排他制御の仕組みそのものはほぼ書き切ってしまったため、分量のバランスとしては悪くなってしまうかもしれませんが・・ 本記事の内容がどなたかの役に立てば幸いです。
アバター
こんにちは、広野です。 Amazon Cognito 認証を使用してアプリから AWS AppSync API を呼び出すとき、デフォルトではリゾルバのみでは Cognito ユーザーの ID や Cognito グループぐらいしか属性を取得できません。 Cognito ユーザーの ID さえわかれば、AWS Lambda 関数で Amazon Cognito の API を呼び出せば他の属性を取得することはできます。ですが、AWS Lambda 関数を使用することでレスポンスが遅くなるのは嫌です。本記事では、AWS Lambda 関数抜きで実現する方法を紹介します。 背景 まず、AWS 公式ドキュメントを読むと。 ※ここでは、JavaScript リゾルバで説明します。VTL も文法は違えど、内容は同じです。 AWS AppSync JavaScript リゾルバーコンテキストオブジェクトリファレンス - AWS AppSync AWS AppSync の JavaScript リゾルバーリファレンス docs.aws.amazon.com 長々と書いてありますが、コンテキストと呼ばれる引数 (ctx) の中に、AWS AppSync がアプリから受け取った情報が格納されます。Amazon Cognito ユーザー関連の情報は ctx.identity の中にあります。 ログに落とすと以下のようなデータが格納されています。 { "identity": { "claims": { "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "device_key": "ap-northeast-1_f074a9ab-30c8-48d2-9ff8-3f265cc90368", "cognito:groups": [ "BASIC" ], "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX", "client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxx", "origin_jti": "bca4c698-3df8-4d2d-a224-ac0095385a98", "event_id": "48eeef11-6aa5-4e77-81f7-69c6e4faaeae", "token_use": "access", "scope": "aws.cognito.signin.user.admin", "auth_time": 1735803851, "exp": 1736061086, "iat": 1736039486, "jti": "317acaa7-fa7b-4ff5-ad32-f0a02b399664", "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }, "defaultAuthStrategy": "ALLOW", "groups": [ "BASIC" ], "issuer": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX", "sourceIp": [ "xxx.xxx.xxx.xxx" ], "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } 一通り見て気付くことは、以下です。 ユーザー ID、ユーザー名、グループ属性はある email 属性が無い カスタム属性が無い 例えばユーザーのメールアドレスを使用した処理をしたいとか、カスタム属性によって処理の実行を制御したいときに、このままでは AWS Lambda 関数が必要になってしまいます。AWS AppSync を使うからには、AWS Lambda 関数は使用したくありません。 こうなっている原因は、説明が後回しになりましたが 赤線を引いた  “token_use”: “access”  という部分で表されています。 AWS AppSync はデフォルトでは JWT トークンの中のアクセストークンが使用され、アクセストークン内に含まれている情報が限られているためにこの状況が発生しています。 比較して、Amazon API Gateway で Cognito オーソライザーを使用するときは ID トークンを使用します。以前私が書いた記事を紹介しますが、リクエストの Authorization ヘッダーに ID トークンを格納して送る、と紹介していました。 Amazon API Gateway Cognito オーソライザーに送信する ID トークン のフォーマットに注意 Amazon API Gateway のセキュリティ機能で、アプリ側で取得した ID トークンが不正なものでないか検証する Cognito オーソライザーという機能があります。API Gateway のタイプにより ID トークンを送信するフォーマットが異なるため、注意点として紹介します。 blog.usize-tech.com 2022.04.07 AWS AppSync でも、ID トークンを使用した Cognito 認証ができれば、アクセストークンよりもユーザー情報が多く格納されるため、この問題を解決できるのでは?と思った次第です。 解決方法 解決方法は、アプリから AWS AppSync API にリクエストを送るときの Authorization ヘッダーに ID トークンを格納することになります。 ヒントとなるドキュメントは、AWS Amplify UI のドキュメントにあります。カスタムヘッダーを設定するコードのサンプルが紹介されています。 Connect your app code to API - AWS Amplify Gen 2 Documentation Learn how to connect your app code to an API. AWS Amplify Documentation docs.amplify.aws ただし、これだけでは具体的にどう書けば今回のユースケースが動くのかわかりませんでしたが、色々と調べた結果動く方法が見つかりました。React の例になりますが、以下のコードになります。 import { Amplify } from 'aws-amplify'; import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react'; import { fetchAuthSession } from 'aws-amplify/auth'; //Cognito, AppSync 連携設定 Amplify.configure( { Auth: { Cognito: { userPoolId: import.meta.env.VITE_USERPOOLID, userPoolClientId: import.meta.env.VITE_USERPOOLWEBCLIENTID, identityPoolId: import.meta.env.VITE_IDPOOLID } }, // 通常は、以下の部分だけを書く。 API: { GraphQL: { endpoint: import.meta.env.VITE_APPSYNC_URL, region: import.meta.env.VITE_REGION, defaultAuthMode: 'userPool', } } }, // 追加で以下のカスタムヘッダーを上とは切り離して書く。(これが重要) { API: { GraphQL: { headers: async () => ({ Authorization: ( await fetchAuthSession() ).tokens?.idToken?.toString() }) } } } ); アプリ内に、使用する Amazon Cognito リソースや AWS AppSync リソースを設定するコードを書くのですが、そこにカスタムヘッダーを追加する設定を書きます。ただし、カスタムヘッダーに格納する値は ID トークンですので、動的に変わります。Amazon Cognito の設定が反映されている前提で、Amazon Cognito にユーザーのセッション情報を問い合わせる fetchAuthSession を実行した結果から ID トークンを抜き出し、格納するコードになっています。 正直内部的な動きはわかっていませんが、こう書くことで AWS AppSync API にリクエストするときのトークンを ID トークンに置き換えることができました。検証した結果、トークンの自動更新も機能していました。 結果 結果、AWS AppSync リゾルバ内で取得するコンテキスト情報がどう変わったかを紹介します。背景の章で紹介したオリジナルのコンテキストと比較するため ctx.identity の中身をお見せします。 { "identity": { "claims": { "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "cognito:groups": [ "BASIC" ], "email_verified": true, "iss": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX", "cognito:username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "origin_jti": "5c010020-e36b-46c5-be5c-579c63b68373", "aud": "xxxxxxxxxxxxxxxxxxxxxxxxxx", "event_id": "24fb81ef-5e04-4d29-ac28-810ce689f7ac", "token_use": "id", "auth_time": 1736259678, "exp": 1736281278, "iat": 1736259678, "custom:zokusei": "test", "jti": "7e980548-297d-4514-bdd0-7862550f6def", "email": "hirono@xxxxx.xxx" }, "defaultAuthStrategy": "ALLOW", "groups": [ "BASIC" ], "issuer": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX", "sourceIp": [ "xxx.xxx.xxx.xxx" ], "sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } 赤線を引いた箇所 が変更点です。 “token_use”: “id” になっており、ID トークンが使われていることがわかります。 “custom:zokusei”: “test” が追加されました。これは私がテスト的に Amazon Cognito に追加したカスタム属性です。これでカスタム属性に応じた制御をかけることができますね。 “email”: “hirono@xxxxx.xxx” が追加されました。これで Cognito ユーザーのメールアドレスを使用した処理ができますね。 カスタム属性については、前提として Amazon Cognito のアプリケーションクライアントの「属性権限」設定で、読み取りを有効にしていないと ID トークン経由で連携されません。ご注意ください。   さらに補足です。 AWS AppSync が受け取るコンテキストには、リクエストヘッダー情報も含まれています。その中の Authorization ヘッダーの中にトークンが格納されているわけですので、そこから base64 でデコードして同じ情報を取得することも可能と思われます。ですが、結局 ctx.identity.claims に含まれている情報と同じなので、意味はないです。 まとめ いかがでしたでしょうか。 AWS Lambda 関数を使いたくない一心で調査を頑張りました。本件の解決については、同僚のメンバーにかなり協力して頂きました。感謝!です。 本記事が皆様のお役に立てれば幸いです。
アバター
こんにちは、SCSKの内ヶ島です。 EC2の課金を気にせずにAmazon Linux 2023(以下、AL2023)を使うため、手元のオンプレミス環境で稼働したいと思ったことはありませんか? 本記事ではオンプレミスのVirtualBox環境でAL2023を起動する方法をまとめました。 はじめに AL2023はオンプレミスの仮想マシン向けにVMイメージが用意されており、公式にはKVM、VMware、Hyper-Vをサポートしています。 サポート外となりますがVirtualBox環境で起動してみます。 ※Amazon Linux 2ではVirtualBoxもサポートしていたのですが、AL2023では外されたようです。 注意事項 PC上の閉じた環境で検証用途で利用するためセキュリティを考慮していません。 とりあえずOSが起動することを確認したのみです。ハイパバイザーの違いによる動作はご自身でご確認ください。 事前に下記のツールを準備してください。 ova形式ファイルを展開するツールやコマンド:7-Zipやtarコマンドなど CDイメージを作成できるツールやコマンド:mkisofsやgenisoimageなど 参考リンク Amazon EC2 以外で Amazon Linux 2023 を使用する - Amazon Linux 2023 Amazon EC2 などの AWS サービス以外の仮想化環境で Amazon Linux 2023 を使用する方法について説明します。 Amazon EC2 docs.aws.amazon.com   事前準備 公式のVMイメージを使って仮想マシンをセットアップします。VMイメージ内にec2-userユーザーが作成されているので、こちらをパスワードでログインできるように設定していきます。 VMイメージの準備 下記リンクよりVMwareのイメージをダウンロードします。 Index of /os-images/2023.6.20241212.0 cdn.amazonlinux.com 7-Zipなどのツールで中身のvmdkファイルを取り出します。 初期設定ブートイメージの作成 新しい仮想マシンの起動に必要な初期設定情報(ホスト名やユーザーデータなど)をseed.isoブートイメージとして作成します。 今回の環境ではホスト名とユーザー情報を設定し、ネットワーク情報はDHCPで取得することにします。 お手持ちのLinux環境(VirtualBox上に別途用意したLinux環境やWSL2)で作業します。 あらかじめmkisofsをインストールしてください。 meta-data ファイルを作成 仮想マシンのホスト名< vm-hostname> の設定をmeta-dataファイルに書き込みます。 echo "local-hostname: <vm-hostname> " > meta-data user-data ファイルを作成 VMイメージにec2-userユーザーが作成されているので、こちらをパスワード認証で接続する設定とします。 パスワードでのSSH接続を許可する設定、ec2-userのパスワード <password> の情報をuser-dataファイルに書き込みます。 cat > user-data <<_EOF_ #cloud-config ssh_pwauth: True chpasswd: list: | ec2-user: <password> expire: False _EOF_ seed.isoブートイメージを作成 meta-dataとuser-dataを使用してseed.isoディスクイメージを作成します。 mkisofs -output seed.iso -volid cidata -joliet -rock user-data meta-data 作成したseed.isoをローカル上にコピーしておきます。 これで仮想マシンを起動する準備ができました。 VirtualBoxの仮想マシン起動設定 作成したseed.isoファイルを仮想化CD-ROMドライブとして、取り出したvmdkファイルをHDDとして起動します。 新規に仮想マシンを作成します。 [仮想マシン] – [新規] 【名前とオペレーティングシステム】 [名前] に任意の名前をつけます。(ここではal2023を指定) [ISOイメージ] に先ほど作成したseed.isoを指定します。 [タイプ] に [Linux] を選択、[バージョン] は [Other Linux (64-bit)] を選択します。 【ハードウェア】 メインメモリーを設定します。 ここでは 2048 MB にしておきます。(ご希望のスペックに変更してください) 【ハードディスク】 [すでにある仮想ハードディスクファイルを使用する] を選択し、取り出したvmdkファイルを指定してください。 [完了] を押します。 これで仮想マシンが設定できました。 PCと同じネットワークから接続したい場合は、[設定] からネットワークアダプターの割り当てを [NAT] から [ブリッジアダプター] に変更します。 AL2023を起動 さっそく起動してみましょう。 Amazon Linuxとしてブートしていることが確認できます。 手持ちの環境では1分以内に起動完了しました。 ログインできるか確認します。 user-dataファイルに記載したパスワードを使用して、ec2-userユーザーでログインできました。 インターネット接続できればdnf(yum)も使えます。 Teraterm等からSSH接続もでき、鳥がお出迎えしてくれます。 さあ、使い放題のAL2023ライフを堪能しましょう。 おわりに EC2で動作するAL2023をオンプレミス環境で起動できました。 初期設定(seed.isoの作成)を手動で行ったことで、AWSでは自動で行われる設定がいかに楽であるか、改めてありがたさを感じた次第です。
アバター
こんにちは、SCSK株式会社の小寺崇仁です。 先日、複数サーバからログが大量に出力された場合に、生成AIを使って分析したいと要望頂きましたので、実現してみたいと思います。 具体的には、「同一ホストグループ」で発生した「直近1日のログ」を生成AIに分析してもらいます。 検証内容 APサーバ(ap_1229)とDBサーバ(db_1229)を準備します。 APサーバからは、DBに接続できない旨のエラーを出力します。 DBサーバからは、MaxConnectionで接続できない旨のエラーを出力します。 Pythonのスクリプトを作成し、同一ホストグループ、直近1日のログを抽出し、生成AIに分析をさせます。 生成AIには以下のプロンプトを送ります。 次のログが出力されました。原因を分析してください Host: ap_1229, Time: 2024-12-29 10:59:18, Value: can't connect to mysql database Host: ap_1229, Time: 2024-12-29 10:59:20, Value: can't connect to mysql database Host: db_1229, Time: 2024-12-29 10:55:23, Value: FATAL: sorry, too many clients already Host: db_1229, Time: 2024-12-29 10:55:47, Value: FATAL: too many connections for database zabbix スクリプト作成 pythonのスクリプトを配置します。pythonのスクリプトも生成AIがベースを作ってくれるので簡単ですね。 import mysql.connector import argparse import json import sys import warnings import boto3 from mysql.connector import Error warnings.simplefilter('ignore') # 引数を処理するための関数 def get_hostid(): parser = argparse.ArgumentParser(description="Get data for a specific hostid.") parser.add_argument('hostid', type=int, help="Host ID to filter the data.") args = parser.parse_args() return args.hostid # データベース接続情報 host = 'localhost' database = 'zabbix' user = 'zabbix' password = 'password' # AWS接続情報 client = boto3.client('bedrock-runtime', region_name='ap-northeast-1', verify=False) model_id = 'anthropic.claude-3-5-sonnet-20240620-v1:0' # SQLクエリ def create_query(hostid): sql_query = f""" SELECT h.host, CONVERT_TZ(FROM_UNIXTIME(hs.clock), '+00:00', '+09:00') AS time, hs.value FROM items i LEFT JOIN hosts h ON h.hostid = i.hostid LEFT JOIN hosts_groups hg ON h.hostid = hg.hostid LEFT JOIN history_str hs ON i.itemid = hs.itemid WHERE h.hostid IN ( SELECT hostid FROM hosts_groups WHERE groupid IN ( SELECT groupid FROM hosts_groups WHERE hostid = {hostid} ) ) AND hs.clock >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 24 HOUR)); """ return sql_query def execute_query(hostid): try: # MySQLデータベースに接続 connection = mysql.connector.connect( host=host, database=database, user=user, password=password ) if connection.is_connected(): cursor = connection.cursor() sql_query = create_query(hostid) cursor.execute(sql_query) # 結果の取得 result = cursor.fetchall() # 結果をリストに格納 result_list = [] for row in result: result_list.append(f"Host: {row[0]}, Time: {row[1]}, Value: {row[2]}") return result_list except Error as e: print(f"エラーが発生しました: {e}") finally: if connection.is_connected(): cursor.close() connection.close() # 実行部分 if __name__ == "__main__": hostid = get_hostid() # コマンドライン引数からhostidを取得 logs = execute_query(hostid) # クエリを実行 prompt = f"次のログが出力されました。原因を分析してください\n" + "\n".join(logs) native_request = { "anthropic_version": "bedrock-2023-05-31", "max_tokens": 512, "temperature": 0.5, "messages": [ { "role": "user", "content": [{"type": "text", "text": prompt}], } ], } request = json.dumps(native_request) response = client.invoke_model(modelId=model_id, body=request) model_response = json.loads(response["body"].read()) #回答の部分のみを抽出 answer = model_response["content"][0]["text"] print(answer) つぎに、ZabbixのWeb管理画面から実行できるようにスクリプトを登録します 実行 障害画面からAPサーバの障害をクリックして、スクリプトを実行します。 スクリプトではAP,DBサーバの直近1日のログを生成AIに分析させます。 ポップアップにて、生成AIの分析結果が表示されます。 出力文字数オーバー?で途中で切れました。。。 コマンドで実行すると以下のレスポンスです。 [root@zb7 externalscripts]# python grouplog.py 11644 このログから、以下のような原因分析が可能です: 1. データベース接続の問題: - APサーバー(ap_1229)がMySQLデータベースに接続できていません。 - これは3回連続で発生しており、持続的な問題であることを示しています。 2. データベースサーバーの過負荷: - DBサーバー(db_1229)で2つの重大なエラーが発生しています。 - "too many clients already" と "too many connections for database zabbix" というエラーは、データベースサーバーが処理できる以上の接続要求を受けているこ とを示しています。 3. タイミング: - DBサーバーのエラーは10:55頃に発生し、その後APサーバーの接続エラーが10:59頃に発生しています。 - これは、DBサーバーの問題がAPサーバーの接続エラーの原因である可能性を示唆しています。 4. 考えられる根本原因: - データベースサーバーのリソース(特に接続数)が不足している。 - アプリケーションが適切にデータベース接続を管理していない(接続をクローズしていない)。 - 突然のトラフィック増加やバッチ処理などによる一時的な負荷増大。 - データベースの設定(最大接続数など)が適切でない。 5. 推奨される対応: - データベースサーバーの最大接続数設定を確認し、必要に応じて増加させる。 - アプリケーションコードを確認し、データベース接続の適切な管理(接続のクローズなど)を確保する。   結果 DBサーバの最大接続数設定や、アプリでのクローズ忘れの可能性がある旨の回答がされました。実用できる回答だと思います。 今回のスクリプトは同一ホストグループのログをまとめて送信しているため、各サーバのログを1台づつ確認する手間が省けます。 最後に 弊社ではZabbix関連サービスを展開しています。以下ページもご参照ください。 SCSK Plus サポート for Zabbix SCSK Plus サポート for Zabbix 世界で最も人気のあるオープンソース統合監視ツール「Zabbix」の導入構築から運用保守までSCSKが強力にサポートします www.scsk.jp ★YouTubeに、SCSK Zabbixチャンネルを開設しました!★ SCSK Zabbixチャンネル 本チャンネルでは、SCSK株式会社でのZabbixに関するトレンド/事例紹介などを動画にまとめて取り上げております。最新のトピックについては、以下の弊社HPもしくはツイッターアカウントをぜひ参照ください。ツイッターアカウント: www.youtube.com ★X(旧Twitter)に、SCSK Zabbixアカウントを開設しました!★ x.com x.com
アバター
こんにちは、SCSK株式会社の小寺崇仁です。 ZabbixでSyslogサーバの監視についてお問い合わせ頂くことがございます。 シンプルな監視方法を検証しましたのでご紹介いたします。 まだ、実績のない方式のため、設定してみた問題点などあれば、イベント等でお声掛けください。 いままでの課題 NW機器等の監視で、Syslogサーバを構築している方は多いかと思います。 ZabbixでSyslogを監視する場合、Syslogサーサーバの「/var/log/syslog.log」のようなファイルをテキストログ監視する事になります。 この場合、ZabbixでErrorなどの文字列で、障害を検知すると、障害の発生ホストがSyslogサーバになります。 実際に問題が発生しているのは、NW機器のため、障害の発生しているホストがすぐに判断できない状態となっておりました。 そのため次の方式で、ホスト名の変換処理を行っておりました。 いままでの方式 弊社では、NW機器が障害の発生ホストになるように、一度Syslogサーバのログとして収集をしたのち、ホスト名を変換して、Zabbix_Senderコマンドで再送する事をしていました。 1.NW機器からSyslogサーバにログを転送 2.Syslogサーバからログを受信 3.Zabbixサーバで障害として検知(障害の発生ホストはSyslogサーバ) 4.アクションを使って、Senderコマンドを実行(ホストにNW機器を指定する) 5.Zabbixサーバで障害として検知(障害の発生ホストはNW機器)   新方式 Syslogサーバの機能で、ログの受信後にコマンドを実行することができるため、Syslogサーバ上でZabbix_Senderコマンドを実行します。 1.NW機器からSyslogサーバにログを転送 2.Zabbix_Senderを使用してZabbixに送信 3.Zabbixサーバで障害として検知(障害の発生ホストはNW機器) アクションを使った再送がなくなるため、すごくシンプルな構成になります。 設定方法 「/etc/rsyslog.conf 」に以下を追記します。 Syslogのフォーマットと受信時にシェルと実行するように定義します。 template(name="syslog" type="list") { property(name="fromhost") constant(value="@") property(name="timereported") constant(value=" ") property(name="hostname") constant(value=" ") property(name="syslogtag") constant(value=" ") property(name="msg") constant(value="\n") } if $fromhost-ip != '127.0.0.1' and $fromhost-ip != '::1' then { action(type="omprog" binary="/tmp/s.sh -i" template="syslog") } ※先頭の「fromhost」がZabbixに登録しているhost名と一致する必要があります。 「/tmp/s.sh」を配置します。 ・1行ずつループして処理を行います。 ・@で分割を行います。var1はホスト名が入ります。var2はログの内容です。 #!/bin/bash while read LINE; do IFS='@' read -r var1 var2 <<< "$LINE" zabbix_sender -z localhost -s "$var1" -k syslog -o "$var2" done 注意点 ・Zabbixに登録したhost名と、DNSやhostsで名前解決したホスト名が一致する必要があります。 ・弊社検証環境では、30行/秒のログを処理できました。それぞれの環境で負荷状況をご確認ください。 ・常駐するシェルのため、シェルの変更時はrsyslogサービスの再起動が必要です。 ・SyslogはZabbixサポートの対象範囲外となるため、Redhat等のOSサポートをご利用ください。 ・本方式は実績がありませんので、自己責任でのご利用をお願いします。実用できた方おりましたら、イベント等でお声掛けください。   最後に 弊社ではZabbix関連サービスを展開しています。以下ページもご参照ください。 SCSK Plus サポート for Zabbix SCSK Plus サポート for Zabbix 世界で最も人気のあるオープンソース統合監視ツール「Zabbix」の導入構築から運用保守までSCSKが強力にサポートします www.scsk.jp ★YouTubeに、SCSK Zabbixチャンネルを開設しました!★ SCSK Zabbixチャンネル 本チャンネルでは、SCSK株式会社でのZabbixに関するトレンド/事例紹介などを動画にまとめて取り上げております。最新のトピックについては、以下の弊社HPもしくはツイッターアカウントをぜひ参照ください。ツイッターアカウント: www.youtube.com ★X(旧Twitter)に、SCSK Zabbixアカウントを開設しました!★ x.com x.com
アバター
こんにちは、SCSK株式会社の小寺崇仁です。 先日行われた AWS re:invent 2024 に現地参加してきました。 その中で、発表された Amazon Q にアシストしてもらう、COBOL から Java への移行について検証したいと思います。 ※プレビュー版なので今後、変更等あるかと思います。   参考URL 以下のサイトを参考にさせていただきました。 https://aws.amazon.com/jp/blogs/news/announcing-amazon-q-developer-transformation-capabilities-for-net-mainframe-and-vmware-workloads-preview/ https://bluinsights.aws/ COBOL の準備 COBOLについてはあまり知識がないので、とりあえず動くHelloWorldを作成しました。 インストール Amazon LinuxにOpenCOBOLをインストールしました。 dnf groupinfo "Development Tools" dnf install gmp-devel dnf install ibdb-devel wget "https://www.osscons.jp/osscobol/files?action=cabinet_action_main_download&block_id=414&room_id=21&cabinet_id=11&file_id=406&upload_id=911"-O opensource-cobol-1.5.2J.tar.gz tar zxvf opensource-cobol-1.5.2J.tar.gz cd opensource-cobol-152J_utf8/ ./configure make make install HelloWorld(hw.cbl ) 000100 IDENTIFICATION DIVISION. 000200 PROGRAM-ID. MAIN. 000300 000400 ENVIRONMENT DIVISION. 000500 CONFIGURATION SECTION. 000600 000700 DATA DIVISION. 000800 WORKING-STORAGE SECTION. 000900 01 WS-HELLOW-MESSAGE PIC X(19) VALUE 'Hellow World kodera'. 001000 001100 PROCEDURE DIVISION. 001200 PERFORM DISPLAY-HELLOW 001300 STOP RUN. 001400 001500 DISPLAY-HELLOW SECTION. 001600 DISPLAY WS-HELLOW-MESSAGE. 001700 EXIT. コンパイル&実行 [root@ip-10-0-2-15 2]# cobc -x hw.cbl [root@ip-10-0-2-15 2]# [root@ip-10-0-2-15 2]# ./hw Hellow World kodera Amazon Q Developer のサブスクリプション登録 手探りで作成したため、手順が漏れてたらすみません。 初期設定 バージニアでAmazon Q Developerを開くと以下の画面になりました。AWS IAM Identity Center で作成しました。 有効化 Enableをクリックして有効化します。 AWS IAM Identity Center でユーザを作成 Amazon Q Developer Pro をサブスクライブ 作成したユーザでサブスクリプション登録します。ユーザーあたりの課金があるのでご注意ください。 Transform settings を有効化 有効化すると、管理画面のURLが作成されます。   移行ジョブ作成 Amazon Q とチャット形式でジョブを作成します。 Cobolを移行したいので、「COBOL」と入力。次にJavaへの移行は5番目なので、「5」と入力します。 ジョブの名前とかはこれでよいか聞かれたので、「OK」と返答します。 ジョブ作成するボタンをクリックします。 ジョブ設定 左のメニューがすべて緑になるように順番に設定します。 Kick off modernization ワークスペースのS3を設定します。KMSの設定を間違えてはまりました。 AWSのアカウントIDを入力します。 承認用のURLが作成されるので、開いて、承認します。 COBOL のソースを配置して、配置パスを入力します。 Generate documentation ドキュメントが作成できるみたいです。 S3に作成されます。 Decompose code ドメイン?をつけるみたいです。「Create Domain」で新しく作成します。 適当に「domain」としています。 作成した「domain」を選択して「Send to Q」を押します。 Refactor code しばらく待つと。。。javaに変換されたコードがS3に作成されます。 Java で実行 作成されたJavaのコードを確認するとSplingBootで作成されていました。そのため、eclipseで実行します。 ソースのインポート 「ファイル」ー>「インポート」を開き、「既存Mavenプロジェクト」を選択します。 S3からダウンロードした、ソースの場所を選択し「完了」をクリックします。 実行 「app-pom」を右クリックして、「実行」ー>「Maven install」をクリックします。 実行するとMavenで必要なライブラリをダウンロードするのですが、見つからないファイルがありエラーとなりました。 エラー調査 以下のファイルがダウンロードできなかったです。 404 Not Found repo.maven.apache.org 調べていくと、「AWS Blu Insights」で使用できるライブラリでした。 これをダウンロードするには、認定&承認が必要なようで、ここで断念しました。 Introduction | AWS Blu Insights bluinsights.aws 今回はここまで。 COBOLのソース変換をするのは「AWS Mainframe Modernization」というサービスがあります。 Amazon Qを使用すれば、チャット形式で「AWS Mainframe Modernization」のジョブの作成(移行)を行うことができます。 上記キャプチャでイメージが伝われば幸いです。
アバター
こんにちは、曽我です。 本記事では、Azure上のWindowsのVirtual Machineをアップグレードする方法について説明します。 事前準備 事前準備として、ローカルのPCに以下のモジュールをインストールします。 AZ CLI インストール(参照: https://learn.microsoft.com/ja-jp/cli/azure/install-azure-cli ) Install-Module -Name Az -AllowClobber -Scope CurrentUser もしセキュリティ上の理由ではじかれてしまった場合は、以下のコマンドを実行して下さい。 Set-Excutionpolicy -executionpolicy remotesigned ※ここではRemoteSignedを指定します。 コマンドの意味は以下を参照してください。 Set-ExecutionPolicy アップグレード用の仮想ディスクを作成 ※本作業は、事前準備の環境があれば、どこでもよいです。 PowerShellを開き、まずは、以下のコマンドでAzure環境へ認証して接続します。 Connect-AzAccount Azureにログインするためのブラウザが表示されます。 表示後に、ログインを行います。 ログイン後、以下の内容を貼り付けて、コマンドを実行します。 # デプロイ先のリソースグループ名 $resourceGroup = "リソースグループ名を指定" # デプロイ先のリージョン名 $location = "リージョン名を指定(例:Japaneast)" # ディスクの名前(任意の名前。仮想ディスクの名前になります) $diskName = "WindowsServer2025UpgradeDisk" # アップグレードするOSのSKUを指定 $sku = "server2025Upgrade" # 共通パラメータ(変更不要) $publisher = "MicrosoftWindowsServer" $offer = "WindowsServerUpgrade" $managedDiskSKU = "Standard_LRS" # 最新のVMイメージのバージョン取得 $versions = Get-AzVMImage -PublisherName $publisher -Location $location -Offer $offer -Skus $sku | sort-object -Descending {[version] $_.Version} $latestString = $versions[0].Version # VMイメージの取得 $image = Get-AzVMImage -Location $location -PublisherName $publisher -Offer $offer -Skus $sku -Version $latestString # マネージドディスクの作成 $diskConfig = New-AzDiskConfig -SkuName $managedDiskSKU -CreateOption FromImage -Location $location #ディスクの参照設定 Set-AzDiskImageReference -Disk $diskConfig -Id $image.Id -Lun 0 #ディスクの作成 New-AzDisk -ResourceGroupName $resourceGroup -DiskName $diskName -Disk $diskConfig ※どんなアップグレードイメージが用意されているか確認する場合は、以下のコマンドで確認できます。 以下以外のOSも用意されています。 Windowsの場合 Get-AzVMImageSku -PublisherName "MicrosoftWindowsServer" -Location "Japaneast" -Offer "WindowsServerUpgrade" Linux(Redhat)の場合 Get-AzVMImageSku -PublisherName "Redhat" -Location "japaneast" -Offer "RHEL" 仮想マシンとディスクとしてマウント (1)Azure上のアップグレードするVMの[設定]-[ディスク]を開き、「既存のディスクのアタッチ」をクリックします。「ディスク名」の列より、先ほど作成したアップグレードの用の仮想ディスクを選択して、「適用」をクリックします。 (2)仮想マシンにログインし、コマンドプロンプトを開き、カレントディレクトリをマウントしたドライブに 移動後、以下のコマンドを実行します。 .\setup.exe /auto upgrade /dynamicupdate disable (3)「Windows Server 2025 Datacenter(Desktop Experience)」を選択し、「Next]をクリックします。 (4)「Accept」をクリックします。 (5)インストール処理が始まります。 (6)「Accept」をクリックします。 (7)無事インストールが完了しました。 ※もともと日本語にローカライズしていてのですが、初期状態(英語)に戻ってますね。
アバター
SCSKの畑です。3回目の投稿です。 分量多めのエントリが続いたので、今回は小ネタです。 アーキテクチャ概要 今回はおおよそアプリケーション部分(下図で言えば Nuxt.js)及び Amazon Cognito に閉じた話になりますが、一応載せておきます。解決策のセクションで AppSync や Lambda についても言及はしますが・・ 小ネタ本題 現在作成している Web アプリケーションでは、上図の通り Cognito でユーザ認証したり、AppSync API を実行している関係上、モジュールとして aws-amplify を使用しています。ここで改めて説明するまでもなく便利に使えています。 例えば、Cognito で認証したユーザの各種属性については、以下のようなコードで簡単に取得することができます。特に、下記コードにおける「custom:approle」のようなカスタム属性についても fetchUserAttributes() の返り値としてまとめて取得できるのが大変便利です。 import { fetchUserAttributes, getCurrentUser } from "aws-amplify/auth"; const userAttributes = await fetchUserAttributes() const currentUser = await getCurrentUser() const user_id = currentUser['username'] const user_fullname = `${userAttributes['family_name']} ${userAttributes['given_name']}` const user_email = userAttributes['email'] const user_approle = userAttributes['custom:approle'] 同様に、ユーザが属している Cognito グループの情報については以下のように取得できます。こちらも簡単なコードですし、リストで取得できるのでユーザが複数グループに属している場合も問題ないのですが・・ import { fetchAuthSession } from "aws-amplify/auth"; const { tokens: cognito_session } = await fetchAuthSession(); const user_belong_group = cognito_session.accessToken.payload['cognito:groups'] 実はこのコードで取得できる情報って、Cognito グループの「グループ名(GroupName)」のみで、「説明(Description)」は取得できません。そもそも「説明(Description)」って何?という話があるかもしれませんが、以下画面の通りグループの説明なり備考なりを記載するような属性のことです。 で、本案件においてはこの属性を Cognito グループの論理名として使用しています。つまり、アプリケーションの画面でグループ名(物理名)の代わりに表示するために aws-amplify 経由で合わせて取得したかったのですが、上記コードでは取得できなかったのでちょっと困りました、というのが今回の話になります。 なお、当初は aws-amplify モジュール依存の問題かと思ってたのですが、ID token の中身を調べてみたところそもそもこの情報が含まれていないことが分かりました。よくよく考えるとそりゃそうだよな、という感じではありますが・・ ID (ID) トークンについて - Amazon Cognito ID トークンの使用 docs.aws.amazon.com もちろん、この属性をグループの論理名として使用すること自体が正直アンチパターン寄りではあるとは思います。入力可能な文字数の多さから見ても、本来はグループ自体の説明なり管理のための情報を入力するような属性でしょうし。ただ、そう思いつつも 他にそのような目的で使用できる属性がない Cognito の外側に「グループの論理名」情報を持たせたくない グループの変更・メンテナンス時に二重メンテナンスが必要になってしまう 将来的にお客さんへの引き継ぎを考えた時にシンプルな情報の持たせ方にしておきたい 他にグループに紐つけて持たせたい情報があれば検討の余地があるが、論理名だけだと尚更・・ グループ名(物理名)自体に論理名の情報を包含してしまうのも手だが、抵抗感がある ユーザ(お客さん)の組織をグループにマッピングする仕様とする予定のため、今後の組織変更等で組織名(論理名)のみが変更になった場合の対応コストが嵩んでしまう という理由から、今回は消極的にこのような設計としています。 よって、Cognito グループの「グループ名(GroupName)」「説明(Description)」の両情報を取得するような仕組みを別に用意する必要がありました。 解決策 と言っても特に捻りのある内容ではなく、単純に Cognito グループ情報取得用の AppSync GraphQL query 及び Lambda を作成しただけです。query 定義自体は @function 句で lambda を紐付けただけなので、Lambda の実装例のみ載せてみます。 ランタイムは Python です。また、list_groups() を実行にあたり、Lambda の実行ロールに「cognito-idp:ListGroups」が必要となります。 def get_all_groups(): try: client = boto3.client('cognito-idp') all_groups = [] response = client.list_groups(UserPoolId = USER_POOL_ID) groups = response.get('Groups', []) for group in groups: all_groups.append({'name': group['GroupName'], 'description': group['Description']}) return all_groups except Exception as e: raise アプリケーションの作り上、特定ユーザが属しているグループの情報を取得するのではなく、グループ名と説明のマッピング一式を取得できた方が潰しが効くためそのような実装としています。 まとめ Cognito グループに設定できる属性の種類、もう少し増えても良いように思うんですがどうなんでしょうね。結局、実用上は Cognito ユーザ側に属性として持たせてしまえば良いのと(他 IdP との連携を考えると尚更)、Cognito 内におけるグループの主な役割が所属ユーザへの IAM ロール割当てになっていることから、他の情報を持たせる必要はないよね?という思想なのかなと想像しますが・・ 本記事の内容がどなたかの役に立てば幸いです。
アバター
SCSKの畑です。2回目の投稿です。 今回は、以下のようなアーキテクチャにおけるAWS AppSync API 実行時の認可について、どのように設計・実装したのかについてまとめました。 前提条件 まず、説明にあたり前提条件について記載しておきます。今回は言及すべき案件のバックグラウンドもとい事情があまりなかったため、簡単にまとめました。図に関しては 初回のエントリ からの再掲です。 AWS AppSync API の呼び出し元 少し分かりづらいのですが、上記アーキテクチャにおいて AWS AppSync の API を呼び出しているのは、以下2つとなります。 アプリケーション(正確にはクライアント端末の Web ブラウザ上で動作しているアプリケーション) AWS Lambda Lambda に関しては必ずしも AppSync API を使用する必要はないのですが、アプリケーションと同一の仕組みを使用した方が効率など含めて都合が良いことが多かったためそのような方針としました。 アプリケーションユーザの管理・認証方式 こちらは図から分かる通り、Amazon Cognito を使用しています。案件によってはお客さんの管理する外部 IdP から連携したりすることもあると思いますが、今回はそのような要件がなかったため完全に Cognito に閉じた方式としています。 また、こちらも図内に記載の通りですが、本アプリケーションを使用するユーザは Cognito で認証されていることが前提となります。 採用した AWS AppSync API の認可方式 以下ドキュメントの通り、現在5種類の認可方式がサポートされています。 API キー認可 : 認証されていない API のスロットリングを制御し、シンプルなセキュリティオプションを提供します。 Lambda 認可 : 関数の入力と出力を詳細に説明するカスタム認可ロジックを有効にします。 IAM 認可 : AWS の署名バージョン 4 の署名プロセスを使用し、IAM ポリシーによるきめ細かなアクセスコントロールを可能にします。 OpenID Connect 認可 : ユーザー認証のために OIDC 準拠のサービスと統合します。 Cognito ユーザープール : Cognito のユーザー管理機能を使用して、グループベースのアクセスコントロールを実装します。 GraphQL API を保護するための認可と認証の設定 - AWS AppSync AWS AppSyncユーザーの認証と認可について学習する。 docs.aws.amazon.com 先述した前提条件を踏まえ、今回は認可方式として IAM を選択しました。 まず、以下理由の通り、APIキー、Lambda、OpenID connect の3つは候補から外れました。 API キー:有効期限が最大1年間であり、毎年キーを更新する必要があるため本番運用に適さない Lambda:認可に際し、複雑なロジックを組む必要のあるような要件がない OpenID connect:連携元の IdP 等が存在しないため考慮外 すると IAM or Cognito ユーザープールの2択となりますが、Lambda から AWS AppSync API を呼び出す場合は(この2択だと)IAM しかありません。実質的に Lambda が AppSync のバックエンドとして動作する(=アプリケーションから直接 Lambda を実行しない)以上、Lambda が直接 Cognito の認証情報を扱う必要がないためです。Cognito によって認証/認可されたユーザが AppSync 経由で Lambda を実行することになるため、認可フローとしても問題ありません。 となると、後は AppSync 側でどちらの方式を使用するかになりますが、 Cognito の場合は上記の通りグループ単位のアクセス制御となります。このため、グループ単位で API のアクセス制御を行うような要件があれば必要になるものの、今回はそのような要件がなかったため、 IAM で統一した方がトータルで効率的と判断しました。 AWS IAM ポリシーの権限設定 ということで、まずは IAM ポリシーの権限設定ですが、こちらは以下ドキュメントの通りです。下記設定例では Resource 句をドキュメント通りに厳密に設定していますが、特定の AppSync API 配下の query や mutation などを全て実行できるようにするだけであれば 「”arn:aws:appsync:${Region}:${Account}:apis/${GraphQLAPIId}/*”」としてしまっても良いと思います。 AWS AppSync のアクション、リソース、および条件キー - サービス認可リファレンス Word へのアクセスを制御するために IAM ポリシーで使用できるサービス固有のリソースやアクション、条件キーを一覧表示します AWS AppSync。 docs.aws.amazon.com { "Version": "2012-10-17", "Statement": [ { "Sid": "1", "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:${Region}:${Account}:apis/${GraphQLAPIId}", "arn:aws:appsync:${Region}:${Account}:apis/${GraphQLAPIId}/types/*/fields/*", ] } ] } Lambda については、上記内容を含む IAM ロールを実行ロールに割り当てるだけで権限上は OK です。 AWS Cognito における IAM ロールとの紐付け 一方、アプリケーションから AppSync API を実行する場合、 本案件においては Cognito でユーザ認証されていることを前提としています。つまり、Cognito 経由で付与される IAM ロールに先述した IAM ポリシーの内容が含まれていれば権限上 OK ということになります。 Cognito 経由でアプリケーションユーザに IAM ロールを付与する方法は大別して以下の2パターンあるかと思いますが、先述の通りグループ単位で IAM 権限を制御する要件がなく、純粋なアプリケーション上の権限管理のみに使用しています。そのため、前者の場合は IAM ロール付与用/アプリケーション上の権限管理用という用途の異なるグループが併存してしまうため、 後者 の方がスマートと判断しました。 ユーザプールのグループに IAM ロールを割り当てて、そのグループにユーザを追加 ユーザプールと ID プールを紐付けた上で、ID プールの「認証されたアクセス」に IAM ロールを割り当て 1点、ID プールに割り当てる IAM ロールには、以下ドキュメントに記載されているような信頼関係の設定が必要なことに留意してください。こちらも設定例載せようと思ったのですが、ほぼドキュメントの内容そのままだったので省略します。 IAM roles - Amazon Cognito Use IAM roles with Amazon Cognito identity pools. docs.aws.amazon.com なお、後者の仕組みにおいて、Cognito ユーザ認証されていないアクセスにも「ゲストアクセス」として IAM ロールを割り当てることができます。こちらについては今回使用していないのですが、今考えてみるとアプリケーションの初期化処理などには有用かもしれません。このあたりの話は別の機会に改めて書きたいと思います。 AWS AppSync における設定(AWS Amplify 使用時) 最後に、冒頭で記載したような AppSync の認可設定をする必要があります。今回 AppSync の構成・設定に Amplify を使用しているため、その設定ファイルの種類ごとに記載しています・・が、後述するようにここでハマりました。 schema.graphql ファイル これは Amplify でアプリケーション開発を行う際に必ず編集するファイルかと思いますので、詳細については割愛します。記法や設定できる認可ルールについては以下ドキュメント等を参照ください。  Customize authorization rules - JavaScript - AWS Amplify Gen 1 Documentation Add authorization rules to your GraphQL schema to control access to your data. AWS Amplify Documentation docs.amplify.aws ここでは、AppSync のデータソースとして使用しているDynamoDB と Lambda 関数の実装例を記載してみます。 アプリケーション開発や動作確認の観点から便利なため、下記例ではAPIキーについても認可対象に追加しています。このように1つの type や query などに対して複数の認可対象を指定することが可能です。 DynamoDB type AccessControl @model @auth(rules: [ {allow: public, provider: apiKey}, {allow: private, provider: iam}, ]) { id: String! @primaryKey groups: [String!] } 認可については @auth 句にて設定しています。許可するオペレーション(read / writeなど)も詳細に指定できますが、今回はそのような要件はないため指定していません。 Lambda 関数 type Query { ExecuteStateMachine(input: ExecuteStateMachineInput!): ExecuteStateMachineResponse @function(name: "<Lambda関数名>") @aws_api_key @aws_iam } Lambda 関数をデータソースとする場合は Query ないしは Mutation を使用することになりますが、その定義として @function 句で Lambda 関数名を指定しています。認可については上記の通り「@aws_api_key」「@aws_iam」のように指定しています。 custom-roles.json ファイル さて、実装観点ではこのセクションが本投稿のメインの内容です。つまりハマりどころでした。 ここまでの内容で AppSync の IAM 認可に必要な設定は9割方完了していますが、この状態でアプリケーション or Lambda 関数から AppSync API を実行すると、以下のような権限エラーが出て失敗してしまいます。(以下はアプリケーションから実行した場合の例) { "path": [ "ExecuteStateMachine" ], "data": null, "errorType": "Unauthorized", "errorInfo": null, "locations": [{ "line": 2, "column": 3, "sourceName": null }], "message": "Not Authorized to access ExecuteStateMachine on type Query" } ただ、先に記載した IAM ポリシー/ロールの設定において必要な権限が設定されているにも関わらず、なぜこのような事象が発生するのかが当初よく理解できなかったこともあり、開発中に本事象が発生した際は難儀しました。。最終的に以下のサイト様の情報に辿り着き、同情報をベースにAmplify 公式ドキュメント等を見返してようやく解決に至った次第です。 Amplify GraphQL APIをLambda Functionから操作する zenn.dev まず、結論から書いてしまうと、schema.graphql ファイルと同じ階層に以下設定で custom-roles.json ファイルを作成し、再度 amplify push することで認可が通るようになりました。 { "adminRoleNames": ["arn:aws:sts::${Account}:assumed-role"] } 何故これで上手くいくようになったのかですが、まずは以下 Amplify 公式ドキュメントの内容を見てみましょう。 Customize authorization rules - JavaScript - AWS Amplify Gen 1 Documentation Add authorization rules to your GraphQL schema to control access to your data. AWS Amplify Documentation docs.amplify.aws IAM-based @auth rules are scoped down to only work with Amplify-generated IAM roles. To access the GraphQL API with IAM authorization within your AppSync console, you need to explicitly allow list the IAM user’s name. Add the allow-listed IAM users by adding them to amplify/backend/api/<your-api-name>/custom-roles.json. (Create the custom-roles.json file if it doesn’t exist). Append the adminRoleNames array with the IAM role or user names: { “adminRoleNames”: [“<YOUR_IAM_USER_OR_ROLE_NAME>”] } するといきなり衝撃的なことが書いてあります。曰く、schema.graphql ファイルの項目で記載したような @auth のような記法はあくまでも Amplify が生成した IAM ロールに対して有効であり、任意の IAM ロールを指定したい場合は先述したような内容のファイルを作成する必要があるとのこと。(ドキュメント内では AppSync コンソールから API を実行したい場合の設定という記載ですが) そもそも、Amplify によって作成された IAM ロールなるものに心当たりがなかったので、AWS マネジメントコンソールから探してみたところ、確かに API 名の付いている IAM ロールができているんですよね。 このへんに気付けなかったのは、今回 Cognito を Amplify の構成対象外としていることも影響してるかもしれません。IAM ロールの名前や生成されるパイプラインリゾルバの設定等からも、ID プールの「認証されたアクセス」「ゲストアクセス」にそれぞれ割り当てられる想定のものなのかなと推測します。 とは言うものの、IAM ロールの中身を見る限りは普通に AppSync 関連の権限設定が入っているだけだったので、どこに有意な差があるか分からなかったので、Amplify が構成した スキーマ内のパイプラインリゾルバの中身を改めて見た所、その疑問が氷解しました。 まず、Before mapping template で authRole や unAuthRole の ARN が定義されています。 実際にパイプラインリゾルバ実行時の認可を司っているのは、末尾に「auth0Function」という名前の付いている関数になるようです。こちらも中身を見たところ、要するに $context.identity.userArn が authRole と同一だった場合に認可される(=Amplify が生成した IAM ロールと同一かを判定している)ことが分かりました。つまりこれが有意な差であり、この判定を通すために冒頭で記載したようなワークアラウンドが必要となると理解しました。 AWS AppSync のリゾルバーのマッピングテンプレートのコンテキストリファレンス - AWS AppSync AWS AppSync のリゾルバーのマッピングテンプレートのコンテキストリファレンス docs.aws.amazon.com 実際に adminRoleNames の設定を追加してみると、 このように、Before mapping template にて adminRoles の定義が追加されています。 認可のロジックについても変更されており、$context.identity.userArn が adminRoles で定義されている ARN を含んでいれば、パイプラインリゾルバ実行が認可されるようになっています。では、adminRoles に Lambda の実行ロール及び Cognito ID プールの「認証されたアクセス」に割り当てた IAM ロールの ARN を指定すれば良いのかというと、そうではありません。 先に示した Before mapping template における authRole や unAuthRole の定義を見ると分かる通り、これらの ARN は IAM ロール自身のものではなく、Cognito ID プールでの認可時にAssumeRole により発行される一時クレデンシャルのものになっています。Lambda 実行時も同様に一時クレデンシャルが発行されるため、どちらの場合も IAM ロール自身の ARN とは異なってくるためです。 ただ、ARN は以下の通り「arn:aws:sts::${Account}:assumed-role」から始まるため、それを adminRoleNames の定義として指定してあげれば良いという結論となります。この設定追加により、無事にアプリケーション及び Lambda から AppSync API が実行できるようになりました。 Lambda arn:aws:sts::${Account}:assumed-role/LambdaExecutionRole/${Session} Cognito arn:aws:sts::${Account}:assumed-role/${IAMRoleName}/CognitoIdentityCredentials 今回は触れませんが、Amplify gen2 ではスキーマ定義の記法も含めて大きく変わっているようです。 Clarity Request: Unexpected "Not Authorized" with IAM and Transformer v2 · Issue #100 · aws-amplify/amplify-category-api Which Category is your question related to? GraphQL Transformer v2 Amplify CLI Version 7.6.22 Summary of Problem Since m... github.com まとめ AppSync の認可に IAM を使用するという方針そのものはそれほど時間を要さずに決められたのですが、どのように設計・実装するかという部分についてはハマった点も含めて FIX するのに時間を要しました。一方で、AppSync のパイプラインリゾルバの中身や AssumeRole などの仕組みについてイメージでしか掴めていなかった部分も含めて理解が深まったことは、今後の案件対応を考えると非常に良かったと思っています。 本記事の内容がどなたかの役に立てば幸いです。
アバター
こんにちは、広野です。 ちょっと必要に迫られて、タイトル通りの機能をつくったので紹介します。 背景 HTML の要素で、音声ファイルを埋め込んで再生する audio タグがあります。 : 埋め込み音声要素 - HTML: ハイパーテキストマークアップ言語 | MDN は HTML の要素で、文書内に音声コンテンツを埋め込むために使用します。この要素は、1 つまたは複数の音源を含むことができます。音源は src 属性または 要素を使用して表し、ブラウザーがもっとも適切な音源を選択します。また、 Media... developer.mozilla.org HTML ネイティブの要素なので多くのブラウザで使用可能なのですが、ブラウザによって見た目や機能が変わってしまいます。再生速度は例えば Windows の Chrome ではネイティブ実装されているのですが、iOS の Safari では実装されていません。 以下が audio 操作画面の例です。 これではユーザーの環境によって提供機能が変わってしまうため、今回はブラウザを問わず再生速度を変えられる機能をつくりたいと思いました。 つくったもの audio 操作画面の上部に、1.0x など再生速度を選択するプルダウンメニューを作成しています。 0.5x、0.75x、1.0x、1.25x、1.5x の 5 パターンを用意し、プルダウンで変更すると再生速度が即時反映されます。 仕組み audio は playbackRate という再生速度の設定を持っています。当然デフォルトは 1.0 倍です。 HTMLMediaElement: playbackRate プロパティ - Web API | MDN HTMLMediaElement.playbackRate プロパティは、メディアが再生されるレートを設定します。これはユーザーが早送りやスローモーションなどのユーザー制御を実装するために使用されます。通常の再生レートにこの値を掛けて現在の... developer.mozilla.org これを変更してあげればよいだけなのですが、単純に audio タグの属性に設定できない設定だったのでひと工夫が必要でした。この設定の型が double というものだったので、きちんと型変換して渡してあげる必要がありました。 React コード import { MenuItem, FormControl, Select } from '@mui/material'; //中略 //state定義 const [playbackRate, setPlaybackRate] = useState(parseFloat("1.0")); const [playbackRateOption, setPlaybackRateOption] = useState("1.0"); //中略 //再生速度handleChange const handlePlaybackRate = (event) => { setPlaybackRate(parseFloat(event.target.value)); setPlaybackRateOption(event.target.value); }; //audio再生速度反映 useEffect(() => { const audioElements = document.querySelectorAll('audio'); audioElements.forEach(audio => { audio.playbackRate = playbackRate; }); }, [playbackRate]); //中略 //再生速度プルダウン <FormControl sx={{m:1}} size="small"> <Select value={playbackRateOption} onChange={handlePlaybackRate} autoWidth> <MenuItem value="1.5">1.5x</MenuItem> <MenuItem value="1.25">1.25x</MenuItem> <MenuItem value="1.0">1.0x</MenuItem> <MenuItem value="0.75">0.75x</MenuItem> <MenuItem value="0.5">0.5x</MenuItem> </Select> </FormControl> //中略 //audio操作画面 <audio preload="metadata" controls> <source src={row.uri} type="audio/mp3" /> Your browser does not support the audio. </audio> プルダウンメニュー作成には、 MUI を使用しています。 state を 2 種類定義しています。ひとつは double 型の速度、もうひとつはプルダウン表示用の string 型の速度です。初期設定は 1.0x です。 プルダウンで選択した選択肢の value は一旦 string 型で handlePlaybackRate 関数に渡されますが、その中で parseFloat で double 型に変換したものを playbackRate の state に格納しています。 playbackRate が更新されると発動する useEffect を定義し、その中で audio 要素のプロパティを変更する処理をしています。playbackRate が state なので、この時点で audio 操作画面が再レンダー (つまり、再生速度が画面に反映) されます。 これで再生中でもプルダウンを変えることで再生速度が動的に変わるようになりました! まとめ いかがでしたでしょうか。 なにげに情報が少なかったので、地味に苦労しました。 本記事が皆様のお役に立てれば幸いです。
アバター
こんにちは、広野です。 本記事は、以下の続編記事です。 Storage Browser for Amazon S3 でアクセス可能なバケットを Amazon Cognito グループ単位で動的に変更する [前編] 前編では、Amazon Cognito ID プールの実装を中心に説明します。 blog.usize-tech.com 2024.12.27 前回記事では、Amazon Cognito の実装を中心に説明しましたが、本記事では React 側の設定やコードを説明します。 仕組みの概要 (おさらい) まず、ベースとなる AWS リソースのアーキテクチャは以下です。 Amazon Cognito ユーザーは、グループに所属させる。本記事では、1ユーザーにつき所属するグループは1つのみとする。 Amazon Cognito グループに、グループごとにアクセスを許可するリソースを設定した IAM ロールを関連付ける。 それにより、アプリで Amazon Cognito の認証を受けたユーザーは、所属する Cognito グループの IAM ロールを割り当てられる。 IAM ロールには、そのグループにアクセスを許可する Amazon S3 バケットへのアクセス権限を記述する。 アプリ側では、Amazon Cognito の認証を済ませることにより属性情報の1つとして所属する Cognito グループ名を取得できる。 Cognito グループ名から、ビジネスロジックに応じて Storage Browser for Amazon S3 でアクセスさせる S3 バケットを動的に設定するアプリコードを書く。 React 側の実装 ベースとなる設定、コードについては、以下の記事をご覧ください。これをカスタマイズしていきます。 【re:Invent 2024 発表】Storage Browser for Amazon S3 を React アプリに組み込みました 2024/12/1 に GA されました Storage Browser for Amazon S3 を既存 の S3 と React アプリに組み込みました。(AWS Amplify, Next.js 不使用) blog.usize-tech.com 2024.12.09 やりたいことは、Amplify.configure で設定していた Amazon S3 バケットをユーザーが所属している Cognito グループによって動的に変えたい、でした。しかし、Amplify.configure の中で Amazon Cognito の設定と Amazon S3 バケットの設定を同時に実行しているため、この状態では Cognito グループ情報が取得できていない状態で Amazon S3 バケットの設定をしています 。 App.jsx 変更前コード App.jsx (必要なところだけ抜粋) import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react'; import { Amplify } from 'aws-amplify'; import Loggedin from './Loggedin.jsx'; import Notloggedin from './Notloggedin.jsx'; //Cognito, S3 連携設定 Amplify.configure({ Auth: { Cognito: { userPoolId: import.meta.env.VITE_USERPOOLID, userPoolClientId: import.meta.env.VITE_USERPOOLWEBCLIENTID, identityPoolId: import.meta.env.VITE_IDPOOLID } }, Storage: { S3: { bucket: import.meta.env.VITE_S3BUCKETNAMEAMPLIFYSTG, region: import.meta.env.VITE_REGION, buckets: { amplifystorage: { bucketName: import.meta.env.VITE_S3BUCKETNAMEAMPLIFYSTG, region: import.meta.env.VITE_REGION }, //以下で、Storage Browser でアクセスしたいバケットの設定をしている(固定値) kbdatasource: { bucketName: import.meta.env.VITE_S3BUCKETNAMEKBDATASRC, region: import.meta.env.VITE_REGION, paths: { "bot1/*": { authenticated: ["write", "list", "delete"] }, "bot2/*": { authenticated: ["write", "list", "delete"] } } } } } } }); //ログインチェック function Authcheck() { const { authStatus } = useAuthenticator((context) => [context.authStatus]); return (authStatus === "authenticated") ? <Loggedin /> : <Notloggedin />; } export default function App() { return ( <Authenticator.Provider> <Authcheck /> </Authenticator.Provider> ); } そのため、以下のロジックを考えました。 Storage Browser を使用する直前に、Cognito グループの情報を取得した状態でもう 1 回 Amplify.configure を実行すればよいのではないか? 以下の順で処理を実行します。 1回目: 従来通り、App.jsx 内で Amplify.configure を実行するが、 Storage Browser に関する設定は除く。 1回目の Amplify.configure 実行により、Amazon Cognito にアクセスできる状態になっているため、Cognito グループ情報を取得し、それによってアクセス可能な Amazon S3 バケットを判定する処理を実行する。 2回目: Storage Browser を使用するコンポーネント内で再度 Amplify.configure を実行する。ここに、 Storage Browser の設定を含める。 Amplify.configure は App.jsx などアプリ内のルートに近い位置で実行することが推奨されています。実行する位置にもよるのですが、一度実行するとアプリ全体でその設定が有効になります。そのため、2回実行する例を見たことが無かったのですが、今回実行してみたらうまくいきました。ただし、初回実行時の設定を 2 回目の実行時にも含めておかないと、初回で実行した設定が上書きされてしまうようです。(設定を追記してくれる仕様ではない) App.jsx 変更後コード 以下の考えに従い、修正します。 1回目: 従来通り、App.jsx 内で Amplify.configure を実行するが、 Storage Browser に関する設定は除く。 import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react'; import { Amplify } from 'aws-amplify'; import Loggedin from './Loggedin.jsx'; import Notloggedin from './Notloggedin.jsx'; //Cognito, S3 連携設定 Amplify.configure({ Auth: { Cognito: { userPoolId: import.meta.env.VITE_USERPOOLID, userPoolClientId: import.meta.env.VITE_USERPOOLWEBCLIENTID, identityPoolId: import.meta.env.VITE_IDPOOLID } }, Storage: { S3: { bucket: import.meta.env.VITE_S3BUCKETNAMEAMPLIFYSTG, region: import.meta.env.VITE_REGION, buckets: { amplifystorage: { bucketName: import.meta.env.VITE_S3BUCKETNAMEAMPLIFYSTG, region: import.meta.env.VITE_REGION }, //以下で、Storage Browser でアクセスしたいバケットの設定をしている /* Storage Browser 用の設定を取り除きます。 kbdatasource: { bucketName: import.meta.env.VITE_S3BUCKETNAMEKBDATASRC, region: import.meta.env.VITE_REGION, paths: { "bot1/*": { authenticated: ["write", "list", "delete"] }, "bot2/*": { authenticated: ["write", "list", "delete"] } } } */ } } } }); //ログインチェック function Authcheck() { const { authStatus } = useAuthenticator((context) => [context.authStatus]); return (authStatus === "authenticated") ? <Loggedin /> : <Notloggedin />; } export default function App() { return ( <Authenticator.Provider> <Authcheck /> </Authenticator.Provider> ); } Cognito グループ情報の取得 以下の考えに従い、コーディングします。 1回目の Amplify.configure 実行により、Amazon Cognito にアクセスできる状態になっているため、Cognito グループ情報を取得し、それによってアクセス可能な Amazon S3 バケットを判定する処理を実行する。 ログイン成功以降のコンポーネント内で、例として以下のコードを書くことで Cognito グループは取得できます。 import { useState, useEffect } from 'react'; import { useAuthenticator } from '@aws-amplify/ui-react'; import { fetchAuthSession } from 'aws-amplify/auth'; //ログイン後の画面 const Loggedin = () => { //ステート定義 const [username, setUsername] = useState(); const [authToken, setAuthToken] = useState(); const [groups, setGroups] = useState(); //ユーザ情報取得 const { user, signOut } = useAuthenticator((context) => [context.user]); //セッション情報取得関数 const getSession = async () => { const { tokens } = await fetchAuthSession(); setUsername(tokens?.idToken?.payload.email); //メールアドレスを取得しています。 setGroups(tokens?.idToken?.payload["cognito:groups"]); //Cognitoグループ名を取得しています。(配列になります) setAuthToken(tokens?.idToken?.toString()); //IDトークンを取得しています。 }; //画面表示時実行 useEffect(() => { getSession(); }, []); //以降省略 groups というステートに Cognito グループ名が配列で格納されるので、それをもとに、どの Amazon S3 バケットにアクセス許可するか判定するビジネスロジックは別途必要です。 Storage Browser 周辺のコード 以下の考えに従い、Storage Browser を表示する画面のコードを修正します。 2回目: Storage Browser を使用するコンポーネント内で再度 Amplify.configure を実行する。ここに、 Storage Browser の設定を含める。 前段で Cognito グループ情報をもとにアクセス可能とする Amazon S3 バケットが確定している前提で、Amazon S3 バケット名を以下のサンプルコード内ではハードコーディングします。実際には動的に値が変わる想定です。 import { Amplify } from 'aws-amplify'; import '@aws-amplify/ui-react-storage/styles.css'; import { createAmplifyAuthAdapter, createStorageBrowser } from '@aws-amplify/ui-react-storage/browser'; const Ragkb = (props) => { //state初期化 2回目の Amplify.configure は1回だけ実行すればよいので実行済みフラグを作成 const [isAmplifyConfigured, setIsAmplifyConfigured] = useState(false); //Cognito, S3 連携設定 const amplifyConfigure = () => { Amplify.configure({ //2回目の Amplify.configure 実行時も Cognito の設定は必要 Auth: { Cognito: { userPoolId: import.meta.env.VITE_USERPOOLID, userPoolClientId: import.meta.env.VITE_USERPOOLWEBCLIENTID, identityPoolId: import.meta.env.VITE_IDPOOLID } }, Storage: { S3: { bucket: import.meta.env.VITE_S3BUCKETNAMEAMPLIFYSTG, region: import.meta.env.VITE_REGION, buckets: { amplifystorage: { bucketName: import.meta.env.VITE_S3BUCKETNAMEAMPLIFYSTG, region: import.meta.env.VITE_REGION }, //ここで、1回目の Amplify.configure 実行時には無かった Storage Browser 用の S3 バケットを設定 kbdatasource: { bucketName: 'example-xxx-kbdatasource', //バケット名を入れる、ここが動的に変わる想定 region: import.meta.env.VITE_REGION, //フォルダも同様に動的に変更は可能 paths: { "bot1/*": { authenticated: ["write", "list", "delete"] }, "bot2/*": { authenticated: ["write", "list", "delete"] } } } } } } }); //2回目の Amplify.configure を実行したら、実行済みフラグを true にする setIsAmplifyConfigured(true); }; //Storage Browser const { StorageBrowser } = createStorageBrowser({ config: createAmplifyAuthAdapter() }); //画面表示時 useEffect(() => { //Amplify.configure 再実行、ただしフラグを確認して1回だけ実行する if (!isAmplifyConfigured) { amplifyConfigure(); } }, []); return ( <React.Fragment> {/* 中略 */} {/* 2回目の Amplify.configure が実行済みでないと Storage Browser を表示させない */} {(setIsAmplifyConfigured) && <StorageBrowser />} {/* 中略 */} </React.Fragment> ); }; export default Ragkb;   ここまで実行できると、、、想定通り Storage Browser を表示することができました! 以上です! まとめ いかがでしたでしょうか。 本記事では、前編で紹介した実装を前提に、React 側のコードを紹介しました。今現在公開されている情報の中で、なんとか工夫してやりたいことを実現してみました。Storage Browser for Amazon S3 の今後の機能拡張やドキュメント充実を引き続きウォッチしつつ、よりよい設計を考えたいと思いました。 本記事が皆様のお役に立てれば幸いです。 関連記事 【re:Invent 2024 発表】Storage Browser for Amazon S3 を React アプリに組み込みました 2024/12/1 に GA されました Storage Browser for Amazon S3 を既存 の S3 と React アプリに組み込みました。(AWS Amplify, Next.js 不使用) blog.usize-tech.com 2024.12.09 Storage Browser for Amazon S3 でダウンロードを無効にする デフォルトの設定ではセキュリティ的に実用的ではなかったので、少々セキュリティ設定を組み込んでみました。 blog.usize-tech.com 2024.12.12 Storage Browser for Amazon S3 のアクセスログを取得・検索する [AWS CloudTrail 利用] Storage Browser を使用する上で絶対に必要になる、アクセスログを取得する設定を入れました。 blog.usize-tech.com 2024.12.13 Storage Browser for Amazon S3 でアクセス可能なバケットを Amazon Cognito グループ単位で動的に変更する [前編] 前編では、Amazon Cognito ID プールの実装を中心に説明します。 blog.usize-tech.com 2024.12.27
アバター
Amazon WorkSpacesは、AWSが提供するフルマネージド型の仮想デスクトップサービスです。 本記事では、AWS CLIを使用してAmazon WorkSpacesの移行を実施した検証内容をまとめました。 なお、OS環境の再構築に伴うCドライブの初期化やIPアドレスの再配置など、移行に関する注意点は本検証の目的から除外しておりますのでご注意ください。検証内容を試される際には、実際の要件や条件に合っているかをあらかじめご確認いただくことをおすすめします。 移行に関する制限や詳細な挙動については、以下のリンクをご確認ください。   Personal WorkSpace で を移行する WorkSpaces - Amazon WorkSpaces を移行する方法について説明します WorkSpaces。 docs.aws.amazon.com   検証内容 以下の環境を準備して検証を行いました。 移行前環境 バンドル:wsb-3vw4c37lm Standard with Windows 10 and Office 2016 Pro Plus (Server 2016 based) (PCoIP) ※アプリケーション込み 移行後環境 バンドル:wsb-6vttw8tg3 Standard with Windows 10 (Server 2022 based) アプリケーション:wsa-hpgj744f0 Microsoft Office LTSC Professional Plus 2021 補足~アプリケーションについて~ これまで、バンドルは固定されたアプリケーションを含む形で提供されていましたが、新しい仕様により、アプリケーションはバンドルとは別に管理されることとなりました。したがって、新しいアプリケーションを導入する場合は、手動でインストールする必要があります。 そのため、本検証でもバンドル移行後にアプリケーションのインストールを実施いたします。   Amazon WorkSpaces サービスが Microsoft の生産性向上アプリの提供を拡大 aws.amazon.com   また、各OS に対応するアプリケーションは以下の通りです。   Microsoft Office Professional Plus 2016 (32 ビット) Microsoft Office Professional Plus 2019 (64 ビット) Microsoft LTSC Office Professional Plus / Standard 2021 (64 ビット) Microsoft Project Professional / Standard 2021 (64 ビット) Microsoft LTSC Visio Professional / Standard 2021 (64 ビット) Microsoft Visual Studio Professional / Enterprise 2022 Windows Server 2016 アンインストール サポートされません サポートされません サポートされません サポートされません サポートされません Windows Server 2019 サポートされていません アンインストール インストール/アンインストール インストール/アンインストール インストール/アンインストール サポートされていません Windows Server 2022 サポートされていません アンインストール インストール/アンインストール インストール/アンインストール インストール/アンインストール インストール/アンインストール Windows 10 アンインストール アンインストール インストール/アンインストール インストール/アンインストール インストール/アンインストール インストール/アンインストール Windows 11 アンインストール アンインストール インストール/アンインストール インストール/アンインストール インストール/アンインストール インストール/アンインストール   Manage applications in WorkSpaces Personal - Amazon WorkSpaces Learn how to Manage applications. docs.aws.amazon.com   環境準備 AWS CLI のインストールが本検証を進めるうえでの前提条件となります。 AWS CLIではVer.1.29.61およびVer.2.13.25以降でアプリケーションの管理のコマンドが追加されているため、これ以前のバージョンを利用している場合はアップデートが必要となります。  以下のコマンドを使用して、バージョン確認を行います。 aws --version   $ aws --version aws-cli/2.21.3 Python/3.12.6 Windows/11 exe/AMD64   ※AWS は現在、バージョン 2 の使用を推奨しており、また、過去に確認されたバグの修正や、新機能の追加の観点からも最新バージョンの利用を推奨しています。 ※AWS CLIのリージョンの設定や証明書の登録などは環境に応じて実施する必要があります。   WorkSpacesの移行 以下のコマンドを使用して移行を実施しました。 移行の大まかな流れとしては、まずバンドル移行を行い、その後にアプリケーションの関連付けを行います。 バンドルの移行 以下のコマンドを使用して、移行前環境のWorkSpace(以下、移行前WorkSpace)の 状態を確認します。 aws workspaces describe-workspaces --workspace-ids [移行前WorkSpace ID]   $ aws workspaces describe-workspaces --workspace-ids ws-xxxxxxxdq { "Workspaces": [ { "WorkspaceId": "ws-xxxxxxxdq", "DirectoryId": "d-xxxxxxxxxx", "UserName": "test", "IpAddress": "10.0.xxx.xxx", "State": "AVAILABLE", "BundleId": "wsb-3vw4c37lm", "SubnetId": "subnet-xxxxxxxxxx939dca7", "ComputerName": "TEST", "WorkspaceProperties": { "RunningMode": "AUTO_STOP", "RunningModeAutoStopTimeoutInMinutes": 60, "RootVolumeSizeGib": 80, "UserVolumeSizeGib": 50, "ComputeTypeName": "STANDARD", "Protocols": [ "PCOIP" ], "OperatingSystemName": "WINDOWS_SERVER_2016" }, "ModificationStates": [] } ] }   以下のコマンドを使用して、移行後環境のWorkSpace(以下、移行後WorkSpace)の バンドルを指定し、バンドルを移行します。移行後 WorkSpaceのID(”TargetWorkspaceId”)が 新しく発行されるため、 ID を控えておく必要があります。(手順3とアプリケーションの関連付けで必要となります)   aws workspaces migrate-workspace --source-workspace-id [移行前WorkSpace ID] --bundle-id [移行後WorkSpaceのバンドルID]   $ aws workspaces migrate-workspace --source-workspace-id ws-xxxxxxxdq --bundle-id wsb-6vttw8tg3 { "SourceWorkspaceId": "ws-xxxxxxxdq", "TargetWorkspaceId": "ws-xxxxxxxmg" } また、移行後WorkSpaceのID( “WorkspaceId”)は、以下のコマンドでも 確認することができます。 aws workspaces describe-workspaces --directory-id [ディレクトリID] --user-name [ユーザー名]     $ aws workspaces describe-workspaces --directory-id d-xxxxxxxxxx --user-name test { "Workspaces": [ { "WorkspaceId": "ws-xxxxxxxmg", "DirectoryId": "d-xxxxxxxxxx", "UserName": "test", "State": "PENDING", "BundleId": "wsb-6vttw8tg3", "WorkspaceProperties": { "RunningMode": "AUTO_STOP", "RunningModeAutoStopTimeoutInMinutes": 60, "RootVolumeSizeGib": 80, "ComputeTypeName": "STANDARD", "Protocols": [ "PCOIP" ], "OperatingSystemName": "WINDOWS_SERVER_2022" }, "ModificationStates": [] } ] }   以下のコマンドを使用して、移行後WorkSpace の状態を確認し、 State が “AVAILABLE” (使用可能)と表示されることを確認します。(処理に 20分ほどかかることがあります) aws workspaces describe-workspaces --workspace-ids [ 移行後 WorkSpace ID]   ※ State が “PENDING” (保留中)   $ aws workspaces describe-workspaces --workspace-ids ws-xxxxxxxmg { "Workspaces": [ { "WorkspaceId": "ws-xxxxxxxmg", "DirectoryId": "d-xxxxxxxxxx", "UserName": "test", "State": "PENDING", "BundleId": "wsb-6vttw8tg3", "WorkspaceProperties": { "RunningMode": "AUTO_STOP", "RunningModeAutoStopTimeoutInMinutes": 60, "RootVolumeSizeGib": 80, "ComputeTypeName": "STANDARD", "Protocols": [ "PCOIP" ], "OperatingSystemName": "WINDOWS_SERVER_2022" }, "ModificationStates": [] } ] } ※Stateが”AVAILABLE”(使用可能)  $ aws workspaces describe-workspaces --workspace-ids ws-xxxxxxxmg { "Workspaces": [ { "WorkspaceId": "ws-xxxxxxxmg", "DirectoryId": "d-xxxxxxxxxx", "UserName": "test", "IpAddress": "10.0.xxx.xxx", "State": "AVAILABLE", "BundleId": "wsb-6vttw8tg3", "SubnetId": "subnet-xxxxxxxxxx939dca7", "ComputerName": "TEST", "WorkspaceProperties": { "RunningMode": "AUTO_STOP", "RunningModeAutoStopTimeoutInMinutes": 60, "RootVolumeSizeGib": 80, "UserVolumeSizeGib": 50, "ComputeTypeName": "STANDARD", "Protocols": [ "PCOIP" ], "OperatingSystemName": "WINDOWS_SERVER_2022" }, "ModificationStates": [] } ] }   以下のコマンドを使用して、移行前WorkSpaceの状態を確認します。 aws workspaces describe-workspaces --workspace-ids [ 移行前 WorkSpace ID]   ※内容が表示されないため移行が完了したことが確認できます。 $ aws workspaces describe-workspaces --workspace-ids ws-xxxxxxxdq { "Workspaces": [] }   アプリケーションの関連付け 以下のコマンドを使用して、アプリケーションの関連付けの状態を確認します。 aws workspaces describe-workspace-associations --workspace-id [移行後WorkSpace ID] --associated-resource-types APPLICATION   ※内容が表示されないため アプリケーションの関連付けがない ことが確認できます。 $ aws workspaces describe-workspace-associations --workspace-id ws-xxxxxxxmg --associated-resource-types APPLICATION { "Associations": [] }   以下のコマンドを使用して、移行後 WorkSpace とアプリケーションの紐づけを行います。 aws workspaces associate-workspace-application --workspace-id [ 移行後 WorkSpace ID] --application-id [ アプリケーション ID]   ※Stateが”PENDING_INSTALL_DEPLOYMENT”(インストールのデプロイ待ち)状態になることを確認します。 $ aws workspaces associate-workspace-application --workspace-id ws-xxxxxxxmg --application-id wsa-hpgj744f0 { "Association": { "AssociatedResourceId": "wsa-hpgj744f0", "AssociatedResourceType": "APPLICATION", "Created": "2024-12-24T11:19:xx.xxxxxx+09:00", "LastUpdatedTime": "2024-12-24T11:19:xx.xxxxxx+09:00", "State": "PENDING_INSTALL_DEPLOYMENT", "WorkspaceId": "ws-xxxxxxxmg" } }   以下のコマンドを使用して、 アプリケーションをインストールします。 aws workspaces deploy-workspace-applications --workspace-id [移行後WorkSpace ID]   ※Stateが”PENDING_INSTALL”(インストール待ち)状態になることを確認します。                                       $ aws workspaces deploy-workspace-applications --workspace-id ws-xxxxxxxmg { "Deployment": { "Associations": [ { "AssociatedResourceId": "wsa-hpgj744f0", "AssociatedResourceType": "APPLICATION", "Created": "2024-12-24T11:19:xx.xxxxxx+09:00", "LastUpdatedTime": "2024-12-24T11:19:xx.xxxxxx+09:00", "State": "PENDING_INSTALL", "WorkspaceId": "ws-xxxxxxxmg" } ] } }   以下のコマンドを使用して、アプリケーションの関連付けの状態 を確認し、 State が “COMPLETED” (インストール済み) と表示されることを確認します。(処理に3 0分ほどかかることがあります)                            aws workspaces describe-workspace-associations --workspace-id [移行後WorkSpace ID] --associated-resource-types APPLICATION   ※ State が “PENDING_INSTALL”(インストール待ち)       $ aws workspaces describe-workspace-associations --workspace-id ws-xxxxxxxmg --associated-resource-types APPLICATION { "Associations": [ { "AssociatedResourceId": "wsa-hpgj744f0", "AssociatedResourceType": "APPLICATION", "Created": "2024-12-24T11:19:xx.xxxxxx+09:00", "LastUpdatedTime": "2024-12-24T11:19:xx.xxxxxx+09:00", "State": "PENDING_INSTALL", "WorkspaceId": "ws-xxxxxxxmg" } ] } ※ State が “INSTALLING”(インストール中) $ aws workspaces describe-workspace-associations --workspace-id ws-xxxxxxxmg --associated-resource-types APPLICATION { "Associations": [ { "AssociatedResourceId": "wsa-hpgj744f0", "AssociatedResourceType": "APPLICATION", "Created": "2024-12-24T11:19:xx.xxxxxx+09:00", "LastUpdatedTime": "2024-12-24T11:32:xx.xxxxxx+09:00", "State": "INSTALLING", "WorkspaceId": "ws-xxxxxxxmg" } ] } ※ State が “COMPLETED”(インストール済み) $ aws workspaces describe-workspace-associations --workspace-id ws-xxxxxxxmg --associated-resource-types APPLICATION { "Associations": [ { "AssociatedResourceId": "wsa-hpgj744f0", "AssociatedResourceType": "APPLICATION", "Created": "2024-12-24T11:19:xx.xxxxxx+09:00", "LastUpdatedTime": "2024-12-24T11:51:xx.xxxxxx+09:00", "State": "COMPLETED", "WorkspaceId": "ws-xxxxxxxmg" } ] }   注意点 ベストプラクティスとして、複数の WorkSpaces を同時実行で移行する場合、一度に処理できる上限は 25 個とされています。 スクリプトにて移行コマンドを実行する場合には注意が必要です。 WorkSpaces をスクリプトで移行する場合、一度に移行する WorkSpaces の数を25個以下にしてください。   Personal WorkSpace で を移行する WorkSpaces - Amazon WorkSpaces を移行する方法について説明します WorkSpaces。 docs.aws.amazon.com   まとめ Amazon WorkSpaces の移行はマネジメントコンソール上でも試みましたが、移行設定画面にバンドルIDの表示がなく、ID検索の代わりに、マウス操作でフィルタリングをかけて対象のバンドル名を指定する操作となるため、少し手間に感じました。一方で、CLIは使い慣れていないため最初は少し抵抗がありましたが、一括で操作を行えるため、便利さを実感しました。 また、同一の操作を複数のWorkSpacesに対して行う際には、CLIが役立つと考えます。本検証では1台のみを移行したため、マネジメントコンソールとの作業時間に大きな差はありませんでしたが、移行対象が多い場合には、CLIを使用することで時間短縮が見込めると思います。 そして効率化をさらに図るために、本記事で紹介したコマンドを活用し、AWS Lambdaやループ処理を組み合わせて移行する方法も検討できると思いました。
アバター
Amazon Managed Grafana でダッシュボードを構築する際、クエリ作成に苦労していませんか? 複雑なクエリを書こうとすると、構文を覚えたり、条件指定がうまくいかず試行錯誤を繰り返したりと、手間がかかってしまうことがあります。 困っていること: ・構文エラーに悩まされる ・必要なログを絞り込むための条件指定が難しい ・効果的なクエリが思いつかない 本記事では、Amazon CloudWatch Logs Insights クエリジェネレータを活用して簡単にクエリを作成し、Amazon Managed Grafana ダッシュボードを構築する方法を、サンプルを用いてご紹介します。分散システムの遅延調査にお困りの方にも、ぜひ読んでいただきたい内容です。 クエリを手で書くのは大変 Amazon CloudWatch Logs のログデータを Amazon Managed Grafana で可視化するにあたり、 複数条件の組合せを考えたり、大量のログデータから目的のログ抽出構文に悩んだり、クエリ作成の複雑さが課題となることがあります。例えば、特定エラーメッセージの抽出や特定条件によるアクセス数集計などは、慣れないと時間がかかります。もっと簡単にクエリを作成する方法があれば、Grafana ダッシュボード作成の効率が大幅に向上すると思いませんか? もう悩まない!クエリジェネレータで簡単クエリ作成 Amazon CloudWatch Logs Insights クエリジェネレータは、複雑なクエリ作成を簡素化します。マニュアルでクエリ構文を調べたり、試行錯誤を繰り返したりする必要がなく、ダッシュボードを迅速に作成できます。この効率的な方法により、オブザーバビリティを高め、システムの状態をより深く理解することが可能です。さらに、Amazon Managed Grafana のようなオープンソースマネージドサービスと組み合わせることで、より高度な可視化と分析を実現できます。                   画像引用: https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AmazonCloudWatch_0330_v1.pdf Amazon CloudWatch Logs Insightsとは? Amazon CloudWatch Logs Insightsは、大量のログデータをインタラクティブに分析し、クエリを実行できるサービスです。ログデータから特定のパターンや傾向を特定し、迅速なトラブルシューティングやパフォーマンス分析を可能にします。クエリ言語を使用して、ログデータのフィルタリング、集計、ソート、統計計算などを行うことができます。また、検出したログイベントをグラフ化して視覚的に分析することも可能です。これにより、システムの挙動をより深く理解し、隠れた問題を迅速に発見することができます。 CloudWatch Logs Insights を使用したログデータの分析 - Amazon CloudWatch ログ CloudWatch Logs Insights と CloudWatch Logs を使用して、ログデータを検索および分析します。 docs.aws.amazon.com そもそもオブザーバビリティとは? オブザーバビリティとは、システムの内部状態を外部から観察可能なデータ(ログ出力等)により把握する能力のことです。ログ、メトリクス、トレースの3つの柱を組み合わせることで、システムの挙動を詳細に分析し、問題発生時の迅速な対応やパフォーマンスの最適化が可能になります。CloudWatch Logs Insightsは、ログ分析を通じてオブザーバビリティを高めるための重要なツールです。効果的にログを分析することで、システムの健全性をより深く理解し、潜在的な問題を早期に発見することができます。                 画像引用: https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AmazonCloudWatch_0330_v1.pdf オブザーバビリティはなぜ必要なの? 近年、分散システムやマイクロサービスアーキテクチャの増加により、従来の監視手法では、頻繁にデプロイが繰り返され変化するシステムの全体像を把握し、問題発生の原因究明に至るまで時間がかかります。このような状況下で、オブザーバビリティの度合いを高める事で、システムの健全性を維持し、信頼性を向上させるために不可欠な要素となっています。 Amazon Managed Grafana により様々なデータソースからメトリクス、ログ、トレースを集約し、可視化することが容易になります。これにより、システムの全体像を把握し、オブザーバビリティを向上させることができます。 監視(モニタリング)とオブザーバビリティは何が違うの? 監視(モニタリング)は、システムの各種ログや指標をリアルタイムで監視し、あらかじめ設定した閾値を超えるなど異常を検知することを目的とします。一方、オブザーバビリティは、システム全体の状態を観測しながら可視化し、可視化した情報から予期せぬ問題の発見やアプリケーション要求に対する応答遅延の原因究明など、システムの動作を深く理解することに主眼を置いています。つまり、監視は「何かしらの閾値、特定条件を満たしたエラー」を検知するのに対し、オブザーバビリティは「なぜエラーとなったのか」を理解することに重点が置かれています。   監視(モニタリング) オブザーバビリティ 目的 異常の早期発見と対応 問題の原因究明に関する分析 対象 決められた指標 システム全体、複数要素、複数ログデータ 例 特定の閾値を超過したらアラート webサイトの応答が遅い原因を分析し特定する Amazon Cloudwatch Logs クエリジェネレータによるクエリの自動生成 Amazon CloudWatch Logs クエリジェネレータのプロンプトに自然言語により条件を入力、実行することでクエリを簡単に自動生成できます。 この方法により、下記マニュアルのクエリ構文を読み込み、試行錯誤しながらクエリを修正する手間を省略できます。但し、条件が複雑なクエリは自然言語のプロンプト入力に工夫が必要となる場合もあります。 CloudWatch Insights クエリ構文をログに記録する - Amazon CloudWatch ログ CloudWatch Logs Insights では、クエリ言語を使用してロググループをクエリします。クエリ構文は、一般的な関数、算術演算と比較演算、正規表現など、さまざまな関数とオペレーションをサポートしています。 docs.aws.amazon.com クエリの自動生成 まず、Amazon Cloudwatch のログのインサイトをクリックします。               次に、クエリの対象範囲としたいロググループを選択し、Query generatorをクリックします。この時点ではデフォルトでサンプルクエリがセットされています。                   プロンプト入力画面が開くので、どのようなクエリを自動生成してほしいのか条件等を入力し、新しいクエリを生成をクリックします。 成功すると、以下のようにクエリが自動生成されるので(デフォルトのサンプルクエリは自動更新される)、クエリの実行をクリックします。                   クエリの実行結果確認 クエリの実行が完了し、期待する検索結果となっていることを確認します。もし期待値ではない場合は、プロンプト入力欄でクエリを再生成します。 期待値となったクエリは後程使用するので、コピーしておきます。 Amazon Managed Grafanaでダッシュボード構築 Grafana ワークスペース新規作成 ダッシュボード構築を始めるにあたり、ワークスペースの作成を行います。ワークスペースは Grafana インスタンスの論理的な分離単位で、ユーザー、データソース、ダッシュボード、アラート設定などを含む独立した環境です。ワークスペースごとに異なるアクセス権限を設定し、異なるプロジェクトや部門に分けて管理する事ができます。 Amazon Managed Grafana ワークスペースを作成する - Amazon Managed Grafana Amazon Managed Grafana でワークスペースを作成する方法について説明します。 docs.aws.amazon.com Grafanaダッシュボード機能概要 Grafana ダッシュボードは複数のデータソースからのデータ統合、クエリエディタによるデータ取得のカスタマイズ、visualization に対するアラートの追加、ドラッグ&ドロップによるレイアウト調整、開発・本番環境毎に別ダッシュボード管理など複数環境に対応できます。 ダッシュボードの使用 - Amazon Managed Grafana このトピックでは、ダッシュボード機能、ショートカットの概要、およびダッシュボード検索の使用方法について説明します。 docs.aws.amazon.com Grafana に関する詳しい説明はこちら よくわかるGrafana入門【ダッシュボード編①】 | SIOS Tech. Lab はじめに皆さんこんにちは!新卒エンジニアの佐々木千奈です。そろそろ、新卒を名乗れる期間も残りあと2か月を切りまし今回は、Grafanaのパネル操作について学習したので、共有させて頂きます。単純な操作が多いですが、忘備録的な意味も含めて記事に... tech-lab.sios.jp Grafana ダッシュボード新規作成 それでは、前章で自動生成したクエリを使って Grafana にダッシュボードを追加します。 Grafana ワークスペースを作成した際に発行された URL よりログイン、左ペインより Dashboards をクリックし、次に NEW ボタンをクリックします。                     Add visualization をクリックすると Select data source 画面に遷移するので、Grafana でダッシュボードに追加したいデータソースを指定します。                     追加可能なデータソース一覧はこちら ビルトイン データソースに接続する - Amazon Managed Grafana すべての Amazon Managed Grafana ワークスペースで、次のデータソースがサポートされています。 docs.aws.amazon.com Grafana ダッシュボードにパネル追加 新規ダッシュボードにパネルを追加します。まずは、赤枠で表示されているデータソースを変更し、今回は Amazon Cloudwatch Logs を指定します。                     前章でコピーしたクエリを赤枠部分にペーストします。今回は表形式のパネルとしてダッシュボードに追加したいので、右メニューの Visualizations より Table に切り替え、最後に Run queries をクリックします。                     今までは、ここでとても苦労するのですが、Amazon Cloudwatch Logs Insights でクエリの実行結果を確認しているので、Amazon Cloudwatch Logs の可視化に関する作業は効率化されます。 Amazon Cloudwatch Logs Insights で試したクエリ構文が正しく実行され、5つのカラムが表示されています。このパネルをダッシュボードに保存したいので、右上のApplyをクリックします。                     今回は表形式でパネル追加しましたが、多数の visualization  が用意されており、お好みのダッシュボードを試すことができます。 上記の流れを繰り返すと以下のようなダッシュボードが作成できます。                   料金について 2024年12月時点では以下となるようです。 Amazon Cloudwatch Logs Insights:スキャンしたデータ 0.0076USD/1GB Amazon Managed Grafana:Editorライセンス 9USD/1ユーザ Viewerライセンス 5USD/1ユーザ Amazon Cloudwatch Logs Insights のデータソースとして大量のログデータがスキャン対象に含まれる場合、利用料金が高額となる可能性があるので、Amazon Cloudwatch Logs Insights のスキャンデータ量のパネルを追加し、アラートを組み込んだ方がよいかもしれません。 さいごに Amazon Managed Grafana でダッシュボード構築に苦労している方は、Amazon Cloudwatch Logs Insights クエリジェネレータで簡単にクエリを生成し、各種パネルを効率的に作成できます。オブザーバビリティを高め、複雑なシステムの障害原因分析の効率化を実現するために、この方法を試してみてはいかがでしょうか。
アバター
こんにちは、広野です。 Storage Browser for Amazon S3 の記事が意図せずしてシリーズ化されてきました。使い始めると、いろいろとやりたいことができてしまい。紹介する内容はタイトルの通りなので省略します。内容が多いので、一旦前編としてアーキテクチャと Amazon Cognito 周りの実装を紹介します。後編は React アプリ側の実装です。 背景 Storage Browser for Amazon S3 は、皆さんが AWS マネジメントコンソールでバケットやオブジェクトを操作しているようなおなじみの UI を、自分が開発したアプリに組み込める画期的な UI モジュールです。 なんですが、基本的に、アクセス可能なバケットやフォルダは「固定」です。アプリ内で所定の設定を記述するのですが、ユーザーの属性によって動的にアクセス可能なバケットやフォルダを変えようとしたらどうすればいいのだろう?というのが起点でした。実際、そんなニーズが社内開発案件でありました。 実装したいと考えた仕様は、 Amazon Cognito のユーザーをグループに所属させ、グループごとにアクセス可能なバケットを動的に変更したい 、です。 しかし、 公式ドキュメントを読んでも直接的に参考となるドキュメントはありません でした。IAM Identity Center と S3 Access Grants を使用した構成の記述はありますが、そこまで大がかりにしたくありません。Amazon Cognito による制御にとどめたいです。また、ドキュメントにある Customer managed auth というカスタマイズ例を活かしてプログラマティックにアクセス可能なバケットを変えられると期待したのですが、どう書いてもうまくいきませんでした。 (d.items is not iterable というエラーが出るだけ) 将来的には公式のコードサンプル提供や有志の方の検証により、よりスマートな方法が確立されると思いますが、 今時点で私が実現できた方法を紹介します。 Storage Browser for Amazon S3 | Amplify UI for React The StorageBrowser component gives your end users a simple, visual interface for working with data stored in S3. ui.docs.amplify.aws 仕組みの概要 まず、ベースとなる AWS リソースのアーキテクチャは以下です。 Amazon Cognito ユーザーは、グループに所属させる。本記事では、1ユーザーにつき所属するグループは1つのみとする。 Amazon Cognito グループに、グループごとにアクセスを許可するリソースを設定した IAM ロールを関連付ける。 それにより、アプリで Amazon Cognito の認証を受けたユーザーは、所属する Cognito グループの IAM ロールを割り当てられる。 IAM ロールには、そのグループにアクセスを許可する Amazon S3 バケットへのアクセス権限を記述する。 アプリ側では、Amazon Cognito の認証を済ませることにより属性情報の1つとして所属する Cognito グループ名を取得できる。(後編記事で紹介) Cognito グループ名から、ビジネスロジックに応じて Storage Browser for Amazon S3 でアクセスさせる S3 バケットを動的に設定するアプリコードを書く。(後編記事で紹介) Amazon Cognito 側の実装 Amazon Cognito グループによって Cognito ユーザーに割り当てる IAM ロールを設定するには、Amazon Cognito ユーザープールに関連付けた Amazon Cognito ID プールが必要です。Cognito グループの設定はユーザープール内で設定するのですが、ID プールが存在することが前提で機能するので、注意しましょう。 設定方法は、AWS 公式ドキュメント通りです。 Adding groups to a user pool - Amazon Cognito Create user groups in the Amazon Cognito console or with the User Pools API or CLI. docs.aws.amazon.com 開発側の注意点として、 Cognito グループごとに IAM ロールを作成しなければならない ことです。理想は Cognito グループを変数として IAM ロールに記述できればよいのですが、散々調べましたがそのような機能は残念ながらありません。Cognito ユーザを変数にすることはできますが、今回の要件ではないので使用できません。 今回の要件では 1 ユーザーが所属するグループは 1 つのみとしていますが、実案件では複数のグループに所属することは多々あります。そのときに適用される IAM ロールはグループの優先順位の数値に従って決められたグループの IAM ロールになりますので、優先順位の設定に注意が必要です。 ここまでは Cognito ユーザープール側の設定です。 Cognito グループに IAM ロールを関連付けるときの設定として、Cognito ID プール側の設定があります。 ID プールでは、Cognito ユーザーに割り当てる IAM ロールとして標準的に 2 つの IAM ロールを設定します。認証されたロールとゲストロールです。下の画面のように作成、関連付けをしておく必要がありますが、今回の要件では Cognito グループに IAM ロールを関連付けるため、実際のところ使用する予定はないのですが、仕様的に必要になります。 さて、この使用しないと言った 2 つのロールですが、何もしなければこのロールが Cognito ユーザーに割り当てられてしまいます。Cognito グループに関連付けた IAM ロールを割り当てて欲しいので、そのような設定を明示的にする必要があります。 Cognito ID プールの設定画面で以下の設定を見つけます。ここで、 トークンで preferred_role クレームを持つロールを選択する を必ず選択します。 preferred_role は何かというと、Amazon Cognito ユーザーの属性で、そのユーザーに割り当てる IAM ロールを個別に設定するものです。なので Cognito ユーザー単位に設定が可能なのですが、Cognito グループに所属し、かつ Cognito グループに IAM ロールが関連付けられている場合はその IAM ロールが preferred_role として設定されます。つまり、Cognito グループに関連付けられた IAM ロールを使用するにはこの設定を選択する必要があるということです。 そして、ロールの解決の設定は決めなのですが、もしユーザーが Cognito グループに所属していなかったらどうするか?の判断を設定することを意味しています。前述した、認証されたロールを使用させるか、IAM ロールを割り当てないか、です。プログラミングで言う、どの条件にも該当しないときにはこうする、的な処置だと思って下さい。 これら Cognito ID プールから割り当てる IAM ロールの記述は、共通して以下の AWS 公式ドキュメント通りに書きます。 IAM roles - Amazon Cognito Use IAM roles with Amazon Cognito identity pools. docs.aws.amazon.com 具体的な設定は、次章の AWS CloudFormation テンプレートから読み取って頂けたらと思います。少々解説は入れます。 参考: AWS CloudFormation テンプレート 解説をインラインでコメントしておきます。 AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates a Cognito user pool and a Cognito ID pool. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SubName: Type: String Description: System sub name of example. (e.g. prod or test) Default: test MaxLength: 10 MinLength: 1 DomainName: Type: String Description: Domain name for URL. xxxxx.xxx Default: sampledomain.com MaxLength: 40 MinLength: 5 SubDomainName: Type: String Description: Sub domain name for URL. Default: subdomain MaxLength: 20 MinLength: 1 SesId: Type: String Description: Amazon SES ID for sending emails. (email addreess or domain) Default: sampledomain.com MaxLength: 100 MinLength: 5 SesConfigurationSet: Type: String Description: Amazon SES configuration set for sending emails. MaxLength: 100 MinLength: 5 CognitoAdminAlias: Type: String Description: The alias name of Cognito Admin email address. (e.g. Admin) Default: Admin MaxLength: 100 MinLength: 5 CognitoReplyTo: Type: String Description: Cognito Reply-to email address. (e.g. xxx@xxx.xx) MaxLength: 100 MinLength: 5 CognitoEmailFrom: Type: String Description: Cognito e-mail from address. (e.g. xxx@xxx.xx) MaxLength: 100 MinLength: 5 Resources: # ------------------------------------------------------------# # Cognito IdP Roles (IAM) # ------------------------------------------------------------# # 以下の IAM ロールは使用する想定のない「認証されたロール」なので、このサンプルでは設定が適当です。すみません。 CognitoIdPAuthRole: Type: AWS::IAM::Role Properties: RoleName: !Sub example-CognitoIdPAuthRole-${SubName} Description: This role allows Cognito authenticated users to access AWS resources. AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: "sts:AssumeRoleWithWebIdentity" Condition: StringEquals: "cognito-identity.amazonaws.com:aud": !Ref IdPool "ForAnyValue:StringLike": "cognito-identity.amazonaws.com:amr": authenticated Policies: - PolicyName: !Sub example-CognitoIdPAuthRolePolicy-${SubName} PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "lambda:InvokeFunctionUrl" Resource: - Fn::ImportValue: !Sub example-${SubName}-LambdaBedrockArn - Fn::ImportValue: !Sub example-${SubName}-LambdaBedrockAgentArn Condition: StringEquals: "lambda:FunctionUrlAuthType": AWS_IAM # 以下の IAM ロールは使用する想定のない「ゲストロール」なので、このサンプルでは設定が適当です。すみません。 CognitoIdPUnauthRole: Type: AWS::IAM::Role Properties: RoleName: !Sub example-CognitoIdPUnauthRole-${SubName} Description: This role allows Cognito unauthenticated users to access AWS resources. AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: "sts:AssumeRoleWithWebIdentity" Condition: StringEquals: "cognito-identity.amazonaws.com:aud": !Ref IdPool "ForAnyValue:StringLike": "cognito-identity.amazonaws.com:amr": unauthenticated Policies: - PolicyName: !Sub example-CognitoIdPUnauthRolePolicy-${SubName} PolicyDocument: Version: "2012-10-17" Statement: Action: - "s3:ListBucket" Resource: - !Sub "arn:aws:s3:::example-${SubName}-amplifystorage" Effect: Allow # 以下の IAM ロールが Cognito グループに割り当てるものなので意味があります。特定の S3 バケットアクセスを許可しています。 CognitoGroupBasicRole: Type: AWS::IAM::Role Properties: RoleName: !Sub example-CognitoGroupBasicRole-${SubName} Description: This role allows Cognito authenticated users that belong to BASIC group to access AWS resources. AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Federated: cognito-identity.amazonaws.com Action: "sts:AssumeRoleWithWebIdentity" Condition: StringEquals: "cognito-identity.amazonaws.com:aud": !Ref IdPool "ForAnyValue:StringLike": "cognito-identity.amazonaws.com:amr": authenticated Policies: - PolicyName: !Sub example-CognitoGroupBasicPolicy-${SubName} PolicyDocument: Version: "2012-10-17" Statement: - Action: - "s3:ListBucket" Resource: - !Sub "arn:aws:s3:::example-${SubName}-kbdatasource" Effect: Allow - Action: - "s3:DeleteObject" - "s3:PutObject" Resource: - !Sub "arn:aws:s3:::example-${SubName}-kbdatasource/*" Effect: Allow # ------------------------------------------------------------# # Cognito # ------------------------------------------------------------# # Cognito ユーザープールについては、特定の仕様で作成しています。ここでは言及しません。 UserPool: Type: AWS::Cognito::UserPool Properties: UserPoolName: !Sub example-${SubName} MfaConfiguration: "ON" EnabledMfas: - SOFTWARE_TOKEN_MFA Policies: PasswordPolicy: MinimumLength: 8 RequireUppercase: true RequireLowercase: true RequireNumbers: true RequireSymbols: false TemporaryPasswordValidityDays: 180 AccountRecoverySetting: RecoveryMechanisms: - Name: verified_email Priority: 1 AdminCreateUserConfig: AllowAdminCreateUserOnly: false AutoVerifiedAttributes: - email DeviceConfiguration: ChallengeRequiredOnNewDevice: false DeviceOnlyRememberedOnUserPrompt: false EmailConfiguration: ConfigurationSet: !Ref SesConfigurationSet EmailSendingAccount: DEVELOPER From: !Sub "${CognitoAdminAlias} <${CognitoEmailFrom}>" ReplyToEmailAddress: !Ref CognitoReplyTo SourceArn: !Sub arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/${SesId} EmailVerificationMessage: !Sub "example-${SubName} Verification code: {####}" EmailVerificationSubject: !Sub "example-${SubName} Verification code" UsernameAttributes: - email UsernameConfiguration: CaseSensitive: false UserPoolAddOns: AdvancedSecurityMode: "OFF" UserPoolTags: Cost: !Sub example-${SubName} UserPoolClient: Type: AWS::Cognito::UserPoolClient Properties: UserPoolId: !Ref UserPool ClientName: !Sub example-${SubName}-appclient GenerateSecret: false RefreshTokenValidity: 3 AccessTokenValidity: 6 IdTokenValidity: 6 ExplicitAuthFlows: - ALLOW_USER_SRP_AUTH - ALLOW_REFRESH_TOKEN_AUTH PreventUserExistenceErrors: ENABLED SupportedIdentityProviders: - COGNITO CallbackURLs: - !Sub https://${SubDomainName}.${DomainName}/index.html LogoutURLs: - !Sub https://${SubDomainName}.${DomainName}/index.html DefaultRedirectURI: !Sub https://${SubDomainName}.${DomainName}/index.html AllowedOAuthFlows: - implicit AllowedOAuthFlowsUserPoolClient: true AllowedOAuthScopes: - email - openid # ここで、BASIC という名前の Cognito ユーザーグループと、割り当てる IAM ロールを指定しています。 UserPoolGroupBasic: Type: AWS::Cognito::UserPoolGroup Properties: Description: example User Group which allows users able to access basic contents. GroupName: BASIC Precedence: 101 UserPoolId: !Ref UserPool RoleArn: !GetAtt CognitoGroupBasicRole.Arn UserPoolGroupAdmin: Type: AWS::Cognito::UserPoolGroup Properties: Description: example User Group which allows users able to access management tools. GroupName: ADMIN Precedence: 1 UserPoolId: !Ref UserPool # Cognito ID プールの設定です。 IdPool: Type: AWS::Cognito::IdentityPool Properties: IdentityPoolName: !Sub example-${SubName} # ここで、クラシックフローを無効にする設定を入れています。有効になっていると、IAM ロールの割り当てが利かないかもしれません。 AllowClassicFlow: false AllowUnauthenticatedIdentities: false CognitoIdentityProviders: - ClientId: !Ref UserPoolClient ProviderName: !GetAtt UserPool.ProviderName ServerSideTokenCheck: true IdentityPoolTags: - Key: Cost Value: !Sub example-${SubName} IdPoolRoleAttachment: Type: AWS::Cognito::IdentityPoolRoleAttachment Properties: IdentityPoolId: !Ref IdPool Roles: authenticated: !GetAtt CognitoIdPAuthRole.Arn unauthenticated: !GetAtt CognitoIdPUnauthRole.Arn # ここで、Cognito グループに応じて IAM ロールを割り当てるという設定を入れています。 RoleMappings: # CloudFormation で設定するときには、以下の userpool: という項目を任意の名前でいいので差し込まないとエラーになります。 # 設定を複数持てるので、以下の単位で追加できます。IDプロバイダーを Cognito 以外にソーシャルプロバイダーも指定できます。 userpool: IdentityProvider: !Sub cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}:${UserPoolClient} Type: Token AmbiguousRoleResolution: AuthenticatedRole   以上で、前編の Amazon Cognito の実装を終了します。後編では React のコード実装例を紹介します。 Storage Browser for Amazon S3 でアクセス可能なバケットを Amazon Cognito グループ単位で動的に変更する [後編] 後編では React アプリ側の設定、コードを中心に説明します。 blog.usize-tech.com 2024.12.27 まとめ いかがでしたでしょうか。 本記事では、Storage Browser for Amazon S3 の利用で必要となる Amazon Cognito ID プールの実装を中心に説明しました。Storage Browser 特有の内容ではないので、他の要件での参考にもなると思います。 本記事が皆様のお役に立てれば幸いです。 関連記事 【re:Invent 2024 発表】Storage Browser for Amazon S3 を React アプリに組み込みました 2024/12/1 に GA されました Storage Browser for Amazon S3 を既存 の S3 と React アプリに組み込みました。(AWS Amplify, Next.js 不使用) blog.usize-tech.com 2024.12.09 Storage Browser for Amazon S3 でダウンロードを無効にする デフォルトの設定ではセキュリティ的に実用的ではなかったので、少々セキュリティ設定を組み込んでみました。 blog.usize-tech.com 2024.12.12 Storage Browser for Amazon S3 のアクセスログを取得・検索する [AWS CloudTrail 利用] Storage Browser を使用する上で絶対に必要になる、アクセスログを取得する設定を入れました。 blog.usize-tech.com 2024.12.13 Storage Browser for Amazon S3 でアクセス可能なバケットを Amazon Cognito グループ単位で動的に変更する [後編] 後編では React アプリ側の設定、コードを中心に説明します。 blog.usize-tech.com 2024.12.27
アバター
皆さんこんにちは。UGです。 今回は2024/7~2024/10の期間で行われていた、AWS主催のハッカソン型トレーニングの 『 ANGEL Dojo 2024 』 に参加させていただいたので、ANGEL Dojoの概要や自分たちのチームが作ったサービス、感想をお伝えできればと思います! ANGEL Dojoとは 概要 ANGEL Dojo( A WS N ext G eneration E ngineers L eaders Dojo)は、4~6名のメンバーでチームを組み、3カ月間でサービスの企画から開発までを行うハッカソン型トレーニングです。 ユーザー企業とパートナー企業が参加しており、ユーザー企業にとっては内製化支援、パートナー企業にとっては若手育成の機会を主目的として、かつ参加者が 『 AWSを活用したモノづくりを体験することで、開発に必要な知識を習得しプロジェクトで活躍できる人材になる 』 を狙いに実施されていました。 参加条件としてはユーザー企業の方は 経験年数不問 、パートナー企業は 社会人歴1~3年目まで となっています。 チーム構成としては、 ユーザー企業のみで構成された「ユーザー企業チーム」 ユーザー企業1社とパートナー企業1社で構成された「ユーザー・パートナー混合チーム」 パートナー企業2~3社で構成された「パートナー混合チーム」 以上の3つのチームがありました。 今回我々SCSKチームは「パートナー混合チーム」で参加させていただき、 株式会社ゆめみ のメンバーと共に活動を実施しました! ↓↓↓AWS JAPAN APNブログ↓↓↓ [内製化支援推進] ANGEL Dojo 2024 を開始しました! | Amazon Web Services 今年も AWS Next Generation Engineers Leaders Dojo (ANGEL Dojo) が始まりました!この記事では、ANGEL Dojo の概要と、7 月 11 日に実施したキックオフの様子をお届けします。... aws.amazon.com 活動内容と流れ 2024/7/11~2024/10/11までの3か月間が実施期間となっており、毎週の木曜日と金曜日が終日ANGEL Dojoの活動日となっておりました。 木・金曜日共に朝会と夕会があり、木曜日が主にワークショップまたは講義の日で、金曜日がチームでの活動日となっておりました。 朝会夕会は運営の方からのお知らせがメインではございましたが、参加者の中で希望した方による、AWSに関係するお話や、個人の趣味にまつわるお話といった、テーマは自由のLightning Talkも行われておりました! 全体の流れとしては、まず最初の1ヵ月間の『 企画フェーズ 』 、残り2カ月間の『 設計・開発フェーズ 』 の2つのフェーズに分かれてサービスの開発を行っていきました。 「企画フェーズ」では、Amazon社で新しいサービスを開発する際に実施されているWorking Backwardsを利用して、自分たちが開発するサービスのプレスリリースとFAQを作成します。 「設計・開発フェーズ」では、企画したサービスを動く状態にするため、具体的なアーキテクチャを考え、実際に構築をしていきます。 「設計・開発フェーズ」の途中(9月)に中間発表があり、最後に最終発表がありました。 ↓↓↓Working Backwardsとは↓↓↓ Amazonのイノベーションを支える「Working Backwards」とは? ──活用事例やアーキテクチャと合わせて解説 - TECH PLAY Magazine Amazonのイノベーションを生み出すメカニズムである「Working Backwards」。AWSでは顧客の課題を解決するソリューションアーキテクト(SA)がこの考え方を実践している。「AWS Tech talk Night#1」ではWo... techplay.jp 表彰制度について 最終発表において、以下の2つの賞がユーザーチーム・パートナーチームそれぞれで用意されていました。 〇ANGEL賞 参加者/聴講者の皆様から以下の観点で投票 ・ビジネス的に興味深い/価値がある内容か ・ソリューション全体の完成度             〇ベストアーキテクチャ賞 AWS Japanにて選定 ・Well-Architectedなシステムになっているか ・技術的に興味深いチャレンジがあるか ・継続的に改善していきやすいシステムになっているか 参加チームは賞の獲得も1つの目標として活動に励んでおりました! 各賞で1位を獲得した4チームで頂上決戦が実施されました! ※後述しますが我々は1位にはなれなかったので頂上決戦にはいけずでした… ↓↓↓AWS JAPAN APNブログ↓↓↓ [内製化支援推進] ANGEL Dojo 2024 最終発表会の開催・頂上決戦のお知らせ | Amazon Web Services 2024 年 7 月にキックオフを開催した ANGEL Dojo 2024 の最終発表会を 10 月 10 日、11 日にて開催いたしました。この最終発表会にて優秀な成績を収めたチームが 10 月 25 日の頂上決戦イベントに出場されます。... aws.amazon.com ↓↓↓頂上決戦YouTube配信↓↓↓ ゆめみ・SCSKチームが開発したサービス 我々ゆめみ・SCSKチームでは、AIを活用したブレインストーミング支援サービス 『 ブレストナビゲーター 』 略して 『ブレナビ』 を開発しました! このサービスは、ブレストの会話内容に応じた 話題の提案 と、ブレストの会話で出た アイデアの一覧化 を リアルタイムに自動で 行い、ブレストによる新しいアイデアの創出を支援してくれるサービスです! 専用の拡張機能をインストールするのみで、TeamsやZoom、Amazon ChimeなどのWeb会議ツールのブラウザ版で利用することができます! ↓↓↓デモ動画↓↓↓ document.createElement('video'); https://blog.usize-tech.com/wp-content/uploads/2024/12/27806f8a4d7eaa6ddca9d0c0cffa3cb3.mp4 背景 なぜブレストを支援するサービスを開発することになったかというと、我々自身が今回新しいサービスを開発するということでブレストを行いましたが、 何かアイデアあります? ・・・・・ という状況に陥り、 AIに助けてもらう? むしろそれをサービスにしたら良いんじゃない⁉ といった流れから『ブレナビ』を開発することに決めました。 上記のようなブレスト中に会話が止まってしまうといった問題を経験された方は我々以外にも多くいらっしゃると思います。 読者の方もその一人ではないですか?? 会話が止まってしまう、はブレストにおける課題の一つであり、その他にも書記役が必要、否定的な意見を言ってはいけないなど、多くの課題があります。こういったブレストにおける課題を様々な機能を実装することで解決したいという思いでブレナビを開発しました。 機能 ブレナビの機能には、『 会話ログ 』『 ブレログ 』『 話題提供 』の3つがあります。 会話ログ 会話ログは、皆さんもオンライン会議ツールで良く見かけると思いますが、 会話内容をリアルタイムに記録して画面に表示してくれる機能 です。 ブレログ ブレログは、 ブレストの会話で出たアイデアを一覧にまとめて画面に表示してくれる機能 です。 アイデアをAIが自動でまとめてくれるため、書記役が不要になります。 話題提供 話題提供は、 ブレストの会話内容に応じた話題を画面に表示してくれる機能 です。 アーキテクチャ アーキテクチャの全体像は以下の通りとなっています。 今回ブレナビを開発するにいたっては、如何に 無駄なコストをかけず に、かつ リアルタイムに処理 を行えるかがポイントでした。 ですので、全体的な方針として サーバレス や、 マネージドサービス を中心として構築を行いました。 また、最終発表の審査基準にもあるように『 Well-Architectedなシステムになっているか 』もポイントであったため、実際にWell-Architectedの講義を受けてみての知見や、無料で提供されている公式ドキュメントやAWS Well-Architected Toolなどを利用して、アーキテクチャを考えました。 Well-Architectedとは、一言で説明すると『AWSとAWS利用ユーザの経験から作成されたベストプラクティス集』です。 ↓↓↓AWS Well-Architected公式ページ↓↓↓ AWS Well-Architected – 安全で効率的なクラウドアプリケーション AWS Well-Architected Framework では、デベロッパーがより迅速に、かつ低リスクでアプリケーションを構築およびデプロイし、AWS のベストプラクティスに従って情報に基づいた決定をするために役立つガイダンスが提供され... aws.amazon.com 処理の流れ → その他の順にアーキテクチャをご紹介していきます。 音声データを文字データへ変換 まずは会議で取得したユーザーの音声データを文字データに変換、また保存したデータをユーザに提供する部分についてです。 ※図の右上にはWell Architectedのアイコンを表示しており、各機能において該当する柱のアイコンを橙色で色付けして表示しています。 ここでは音声データをAWS FargateとAmazon Transcribeがやりとりを行うことで、文字データに変換し、Amazon Kinesis Data Streamsでデータを後続へ送信しています。 またS3にデータを保存し、署名付きURLという形でユーザへの提供を可能としています。 AWS WAFとAmazon CloudFrontを用いることで音声データを 高速 かつ 安全 に取り込むことを可能としています。 ALBはリアルタイム通信に必要な WebSocketをサポート しており、かつ AWS Fargateともシームレスに統合 されています。 また、長時間の会議にも対応する必要があるため 実行時間の制約がなく 、かつ インフラの管理も不要 となるAWS Fargateのコンテナ(Amazon ECS)を利用しています。 変換データから話題とブレログを生成 次に前段の処理で変換したデータから生成AIを用いて話題やブレログを生成している部分についてです。 前段のKinesis Data Streamsから送られてきたデータをAWS Step Functionsに送り、AWS Step Functionsのフローの中でAmazon Bedrockなどを用いて処理を実施しています。 Amazon Bedrockで生成した話題とブレログをAWS Lambdaを使って後続のAWS AppSyncに送っています。 ここの部分について、開発当初はAWS Step Functionsを使わず、AWS Lambdaのみを使ってのAmazon BedrockやAWS AppSyncへのつなぎ込みを実施しようと考えておりました。(以下は開発当初のイメージ図) ただ我々のチームにはスーパーカリスマーインフルエンサーがおりまして、そのスーパーカリスマインフルエンサー曰く、 とのことで、AWS Step Functionsを利用するようにしました。 そのLambda、本当に必要ですか…?Step Functionsのすゝめ Lambda関数を使わずにStep Functionsのみで完結できるケースについて簡単なアプリケーションの比較を通してご紹介します。 blog.usize-tech.com 2024.09.30 AWS Step Functionsを用いることで、余分なAWS Lambdaの削減を可能とし、 再試行やエラーハンドリングが可能 となっています。 また、Amazon Kinesis Data StreamsがAWS Step Functionsを呼び出すとAmazon Kinesis Data Streams側は処理の完了を待たずに処理を続け、かつAWS Step Functionsの処理が完了したら結果を後続処理のAWS AppSyncにデータを流すようになっております。 つまり、Amazon Kinesis Data StreamsからAWS Step Functionsを呼び出す処理が 非同期処理 となっております。 非同期処理なので、 後続処理が完了するまで待機するといった 無駄なリソース消費や課金がなくなる 先行処理が自分の処理さえ完了すれば終了できるので エラーハンドリングが簡易になる といったメリットを実現しつつ、Bedrockによる話題とブレログのリアルタイム生成を実現しています。 処理結果を画面へ表示 次に処理結果を画面に表示する部分についてです。 ここでは主にAWS Amplifyを用いて構築をしており、元々の音声データを会話ログとして、またAmazon Bedrockで生成した話題とブレログを画面に表示させています。 ブレナビの新しい価値は生成AIをつかって話題を提案したり、ブレログを作成する部分のため、前段の処理の AWS Step Functionsの部分をいかに作りこむか に時間をかける必要がありました。 そのため フロントエンドを簡単に構築 でき、かつ 認証やセキュリティ設定を簡単に実装 できるAmplifyを利用しました。 また リアルタイム性も重要 であったため、 GraphQLに対応しているAWS AppSyncやAmazon DynamoDBを簡単に構築 できるAWS Amplifyはブレナビ開発にぴったりなサービスでした。 セキュリティといった観点では、AWS WAFをよくありがちなAmazon CloudFrontにつけるということをしようとしていました。 しかし、今回の場合S3には静的なコンテンツがあるのみで、真に守るべきはデータベースではないか?というご指摘をメンターの広野さんから受けまして、AWS AppSyncにAWS WAFをつけてデータベースを不正アクセスや攻撃から防御するようにしました。 ↓↓↓AWS Ambassadorメンター広野さん記事一覧↓↓↓ https://blog.usize-tech.com/author/hirono/ blog.usize-tech.com システムの監査 次に監査の部分についてです。 ここではサービスごとにログの出力される場所が異なるため、まずはAmazon CloudWatchにログを集約することで ログの一元化 を実施しています。 このログやメトリクス監視からエラーが発生した際に、SNSを利用して運用担当へ リアルタイムでの通知 をできるようにしています。 また、 ログの長期保管 を実施して、何か問題が発生した際にログが追えるようにしており、かつライフサイクルを設定して 無駄なコストの削減 も実施しています。 認証 次は認証の部分についてです。 ここでは、AWS IAMとAmazon Cognitoを利用して、開発側のユーザとエンドユーザの 安全なユーザ管理 を実現しています。 開発側のユーザには 必要最低限の権限のみ を付与して、思わぬ自己や不正を防止しています。 ブレナビを利用するエンドユーザーはAmazon Cognitoによる安全な認証プロセスを提供しています。 開発環境 最後に開発環境についてです。 今回我々はフロントエンド側もバックエンド側両方ともAmazon CodeCatalystに 開発環境を集約 して開発を行っていました。 GitHubはAWS Amplifyへのデプロイのために使用しており、GitHubとAmazon CodeCatalystを連携させることで、全てAmazon CodeCatalyst上で開発ができます。 Amazon CodeCatalystを利用することで簡単に CI/CDパイプラインの構築が可能 なうえ、アクセス制御や暗号化、セキュリティスキャンの自動化などセキュリティも担保されており、 安全な環境での開発を実現 しています。 結果 結果として以下の通り、「中間発表」と「最終発表」の両方で表彰していただきました。 中間発表:パートナー企業部門 2位 最終発表:パートナー企業部門「ベストアーキテクチャ賞」 2位 もちろん1位を取りに行っていたのと、狙いはANGEL賞だったりがあったので、悔しさの部分も多くありましたが、上記の表彰がいただけたことに非常に満足しています。 自分は大したことはできなかったので、チームメイトやメンターの皆様にただただ感謝でした! ANGEL Dojo 2024 を振り返って 最後に今回の活動を通しての学びと感想を2点まとめさせていただきます。 Working Backwardsを経験して 冒頭で少しだけ触れましたが、今回「企画フェーズ」にてWorking Backwardsを学びました。 自分は今回のANGEL Dojoの活動で初めてWorking Backwardsを学んだため、Working Backwardsがどういったものなのか知りませんでした。 そのため、通常では最後に行うプレスリリースの作成などを、まず最初に行うといった考え方に驚きました。 しかし、最初に「顧客課題を明確にする」ことで、 サービスの企画・開発の目的がブレない といったメリットを理解していきました。 作ってもいないサービスのことを考えるので、ひたすら想像力を働かせなければいけないのは大変でしたが、「このサービスを使うことで何が解決されるの?」「結局このサービスは何がよいの?」といった根本の大事なところを見失わず、作っている途中や後に「このサービスの意味は?」といったことにならないことがメリットだと思いました。 サービス開発を経験して 自分はAWSもド素人でしたが、かつ開発経験も今回が初めてで多くのことを学ばせていただきました。 講義で「アジャイル開発」や「モブプログラミング」などの開発に必要な知識をご教授いただきました。 講義だけでなく、動画も適宜提供してくださったりと運営の方々に手厚くサポートいただきました。 そして実際にチームメンバーとともに開発をさせていただいて、学んだことができたりできなかったりと実際の体験でさらに学ぶことが多くありました。 開発を進めていくうえで、特にタスク分けやコミュニケーションといった メンバー間の連携 が非常に大事 であると思いました。 今回は自分がポンコツすぎ、かつ周りが最強だったのでおんぶにだっこ状態で、こなせるタスク量に差がありましたし、できることの差も多くありました。 だからこそダメダメな自分でもできるタスクを探したり、能力のある人と共に作業をしながらスキルを身に着けていくことが大事だと思いました。 最後に 今回こんな貴重なイベントに参加させていただけたことに本当に感謝しております。 そして、チームメイトやメンターの皆様はもちろんのこと、ANGEL Dojoを運営してくださったAWSの方々、参加者や聴講者の皆様、本当にありがとうございました!!! 本記事でもお伝えした通り、ANGEL Dojoに参加するためには条件がありますが、参加できる条件にある方はぜひ立候補して参加を目指していただければと思います! 最後までお読みいただきありがとうございました!!
アバター
本記事は 新人ブログマラソン2024 の記事です 。 はじめまして!2024年入社新人の織田です。 今回業務にて、人生で初めてPowerShellスクリプトを書く機会がございましたので、今回のスクリプト作成にあたって、学んだことのまとめとして本記事を書かせていただこうと思います。 ただ、今回は基礎的な変数の宣言や、値の代入などの話は省かせていただき、今回私がエラーが発生するなどして躓いたポイントのみにフォーカスしたいと思います。 あまり業務でPowerShellスクリプトを書く機会は多くないかもしれませんが、同様の問題に遭遇した際に解決の一助になれば幸いです。 それでは、始めていきましょう!  動作環境 今回作成したスクリプトの動作環境は下記の通りとなります。 項目 値 実行環境OS Windows Server 2016 Datacenter PowerShell version 5.1 2024年現在、PowerShellの最新バージョンは7.5があり、動作環境によってはバージョンの差による違いもあるかもしれません。 こちらにMicrosoft公式がまとめた「 Windows PowerShell 5.1 と PowerShell 7.x の相違点 」のページがあります。 バージョン7.xを使用中の方はこちらも併せてご確認ください。 遭遇した問題と解決策 ここでは今回のスクリプト作成にあたり、遭遇した問題とその解決策についてまとめていきたいと思います。 章のタイトルを「 エラー 」ではなく「 問題 」としているのは、PowerShellではエラーとして検出されないものもあったためです。 ただ、私のこれまでの経験によって発生した問題なども含まれるため、参考にならない情報も多く含まれているかもしれない点はご了承ください。 今回作成したスクリプトは繰り返しの記述が多く、それらをできるだけなくすために今回は関数を使用しました。 その際、私が遭遇した問題点は下記の3つです。 関数の定義と関数の呼び出しの順番 関数呼び出し時の引数の渡し方 関数名の設定 それぞれについて、詳しく説明していきたいと思います。 関数の定義と呼び出しの順番 PowerShellスクリプト作成中、繰り返しの処理を関数としてまとめようとした際に遭遇した問題です。 一言で言えば、 関数の呼び出しの記述の前に関数を定義しておく必要がある ということです。 ■誤ったスクリプト func "test" function func($a) {   Write-Host $a } ■エラーメッセージ func : 用語 ‘func’ は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから、再試行してください。 発生場所 C:\Users\user-name\test.ps1:1 文字:1 + func “test” + ~~~~ + CategoryInfo : ObjectNotFound: (func:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException ※上記のエラーは「 func 」という名前の関数を定義される前に呼び出した場合のものになります。 正しくは下記の通り、関数を定義してから呼び出しを行う必要があります。 ■スクリプト function func($a) { Write-Host $a } func "test" ■実行結果 PS C:\Users\user-name> .\test.ps1 test まとめると、 原因:関数を定義する前に呼び出していた。 解決策:関数は定義後に呼び出す必要がある。 となります。 関数呼び出し時の引数の渡し方 PowerShellにおける関数の特徴として、定義した関数を呼び出す際の引数の渡し方があります。 PowerShellでは関数を呼び出す際に、「 関数名(引数1, 引数2) 」 や 「 関数名 値1, 値2 」 ではなく 、「 関数名 引数1 引数2 」 というように呼び出します。 この勘違いの厄介なところは、 エラーが出ない ということです。 より正確には、関数定義時に引数を必須しない場合、値が渡されなかった引数には「 $null 」が自動で入ります。 また、PowerShellでは「 (値1, 値2) 」や「 値1, 値2 」という書き方は配列としても認識されるため、上記の誤った呼び出しを行った場合、 一つ目の引数に2つの値が配列として代入され、二つ目の引数に「 $null 」が代入されることになります 。 例として、次のスクリプトの実行結果を挙げます。 ■スクリプト function func($a, $b) { Write-Host $a   Write-Host $b if($b -eq $null) { Write-Host "`$b is null." } else { Write-Host "`$b is not null." } } func ("test1", "test2") Write-Host "---------------------" func "test3", "test4" Write-Host "---------------------" func "test5" "test6" ■実行結果 PS C:\Users\user-name> .\test.ps1 test1 test2 $b is null. --------------------- test3 test4 $b is null. --------------------- test5 test6 $b is not null. となります。実際に、「 func (“test1”, “test2”) 」や「 func “test3”, “test4” 」という引数の渡し方をしたものは、2つ目の引数に値が入っていないことがわかります。 まとめると、 原因:関数を間違った呼び出し方をしてしまっていた。 解決策:原則関数を呼び出す際の引数の書き方は 「 関数名 引数1 引数2 …」 とする。 となります。 関数名の設定 一般的にプログラミングをしていると 予約語 というものに遭遇することがあると思います。 PowerShellにも、予約語は存在しており、変数名や関数名を設定する際には、それを避ける必要があります。 ただ、PowerShellの特徴は関数名を設定する際に、予約語以外にも注意する必要があるということです。 関数名を設定する際に追加で気を付ける必要があるものは、既存のPowerShellコマンドです。 通常、プログラミング言語でコーディングしている際には、予約語以外の既存関数などはインポートなどをしない場合、名前の衝突を気にする必要はありません。 しかし、PowerShellスクリプトやそのほかのシェルスクリプトでは、パスの通っているコマンドがスクリプト中で呼び出すことが可能であり、 PowerShellの場合は特に関数の呼び出し方とコマンドの呼び出し方が同じです 。 そのため、自分で新しく定義したはずの関数が既存のコマンドとして実行され、スクリプトが意図しない処理を実行してしまう恐れがあります。 ■誤ったスクリプト function move($a, $b) { Write-Host "Move", $a, "to", $b } move "test" "test2" ■エラーメッセージ move : パス ‘C:\Users\user-name\test’ が存在しないため検出できません。 発生場所 C:\Users\user-name\test.ps1:4 文字:1 + move “test” “test2” + ~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (C:\Users\user-name\test:String) [Move-Item], ItemNotFoundException + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.MoveItemCommand このエラーはPowerShellに「move」に該当するコマンドがすでに存在するため、定義したmove関数が実行されず、別のmoveコマンドが実行されましたが、引数で指定されたフォルダが存在しないため発生しています。 回避策としては、 同名のコマンドが存在しないことを調べてから関数名を設定する 方法が有効です。 具体的には下記のコマンドを使用します。 gcm <コマンド名> gcm は Get-Command コマンドの略になります 。 実際に gcm で「move」を調べてみると下記のような結果になります。 PS C:\Users\user-name> gcm move CommandType Name Version Source ----------- ---- ------- ------ Alias move -> Move-Item つまり、 Move-Item コマンドのAliasとして「move」が使用されているということです。 代わりの関数名として、例えば「move-alpha」を考えたとして、gcmで確認すると、「move-alpha」に該当するコマンドは存在していないため、 PS C:\Users\user-name> gcm move-alpha gcm : 用語 'move-alpha' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。名前が正しく記述されていることを確認し、パスが含まれている場合はそのパスが正しいことを確認してから、再試行してください。 発生場所 行:1 文字:1 + gcm move-alpha + ~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (move-alpha:String) [Get-Command], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException,Microsoft.PowerShell.Commands.GetCommandCommand という結果になります。 そのため、スクリプトにて「move」を「move-alpha」に変更すると、自分が定義した関数である、 move-alpha が実行できます。 ■スクリプト function move-alpha($a, $b) {   Write-Host "Move", $a, "to", $b } move-alpha "test" "test2" ■実行結果 PS C:\Users\user-name> .\test.ps1 Move test to test2 まとめると、 定義した関数名と同名のコマンドが存在する場合、コマンドの実行が優先される Get-Commandによって同名のコマンドが存在しないことを確認してから関数名を設定する となります。 参考サイト1: about_Reserved_Words – PowerShell | Microsoft Learn (予約語) 参考サイト2: Get-Command (Microsoft.PowerShell.Core) – PowerShell | Microsoft Learn まとめ 以上で今回私が遭遇した関数関係の問題は終わりです。 そのほか、PowerShellにおける関数の詳細な仕様を知りたい方は、下記参考サイトをご参照ください。 参考サイト: about_Functions – PowerShell | Microsoft Learn (関数)   おまけ ここでは今回のスクリプト作成にあたり、有益だと感じた要素をまとめます。 ログの保存 業務に関する処理はログを残しておき、あとから処理が正しく実行されていたかを確認する必要があります。 Start-Transcript と Stop-Transcirpt を使用することで、その間にPowerShellへ出力されたテキスト情報をファイルに保存することができます。 ■スクリプト Start-Transcript Write-Host "a" Write-Host "b" Write-Host "c" Write-Host "d" Stop-Transcript ■実行結果 PS C:\Users\user-name> .\test.ps1 トランスクリプトが開始されました。出力ファイル: C:\Users\user-name\Documents\PowerShell_transcript.device-name.b4xgbacx.20241224150409.txt a b c d トランスクリプトが停止されました。出力ファイル: C:\Users\user-name\Documents\PowerShell_transcript.device-name.b4xgbacx.20241224150409.txt ※今回はトランスクリプトの出力パスとファイル名を指定していないため、実行ユーザーのドキュメントフォルダに「PowerShell_transcript.<デバイス名>.<ランダムな文字列>.<実行日時(yyyymmddHHMMSS)>.txt」という名前でファイルが生成されています。 ■ログファイルの中身 ********************** Windows PowerShell トランスクリプト開始 開始時刻: 20241224150409 ユーザー名: device-name\user-name RunAs ユーザー: device-name\user-name 構成名: コンピューター: device-name (Microsoft Windows NT 10.0.22631.0) ホスト アプリケーション: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -Command Import-Module 'c:\Users\user-name\.vscode\extensions\ms-vscode.powershell-2024.4.0\modules\PowerShellEditorServices\PowerShellEditorServices.psd1'; Start-EditorServices -HostName 'Visual Studio Code Host' -HostProfileId 'Microsoft.VSCode' -HostVersion '2024.4.0' -BundledModulesPath 'c:\Users\user-name\.vscode\extensions\ms-vscode.powershell-2024.4.0\modules' -EnableConsoleRepl -StartupBanner "PowerShell Extension v2024.4.0 Copyright (c) Microsoft Corporation. `https://aka.ms/vscode-powershell Type 'help' to get help. " -LogLevel 'Normal' -LogPath 'c:\Users\user-name\AppData\Roaming\Code\User\globalStorage\ms-vscode.powershell\logs\1735016531-a08696d6-c03f-44f0-884a-6074e122f4c11735016528163' -SessionDetailsPath 'c:\Users\user-name\AppData\Roaming\Code\User\globalStorage\ms-vscode.powershell\sessions\PSES-VSCode-21068-641835.json' -FeatureFlags @() プロセス ID: 22968 PSVersion: 5.1.22621.4391 PSEdition: Desktop PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.22621.4391 BuildVersion: 10.0.22621.4391 CLRVersion: 4.0.30319.42000 WSManStackVersion: 3.0 PSRemotingProtocolVersion: 2.3 SerializationVersion: 1.1.0.1 ********************** トランスクリプトが開始されました。出力ファイル: C:\Users\user-name\Documents\PowerShell_transcript.device-name.b4xgbacx.20241224150409.txt a b c d ********************** Windows PowerShell トランスクリプト終了 終了時刻: 20241224150409 ********************** ※今回VSCode中で実行しているため、関連した出力が含まれています。 参考サイト1: Start-Transcript (Microsoft.PowerShell.Host) – PowerShell | Microsoft Learn 参考サイト2: Stop-Transcript (Microsoft.PowerShell.Host) – PowerShell | Microsoft Learn   サマリの出力 処理の内容をサマリとしてまとめて簡単に把握できるようにすると結果を確認するとき非常に便利です。 実現するには、 $Data = New-Object PSObject | Select-Object Data1, Data2, ... $Data.Data1 = "text1" $Data.Data2 = "text2" ... $Summary += $Data という書き方をし、配列として格納したのち、 $Summary | Format-Table とすることで、テーブル形式でデータを表示することができます。 ■スクリプト $Summary = @() $PrimeMinisters = (("Ishida", "Shigeo", 86), ("Kishimoto", "Fumi", 1095), ("Sugawara", "Yoshio", 385)) foreach ($PrimeMinister in $PrimeMinisters) {   $Data = New-Object PSObject | Select-Object FirstName, LastName, Days   $Data.FirstName = $PrimeMinister[1]   $Data.LastName = $PrimeMinister[0]   $Data.Days = $PrimeMinister[2]   $Summary += $Data } $Summary | Format-Table ■実行結果 PS C:\Users\user-name> .\test.ps1 FirstName LastName Days --------- -------- ---- Shigeo Ishida 86 Fumi Kishimoto 1095 Yoshio Sugawara 385 参考サイト1: New-Object (Microsoft.PowerShell.Utility) – PowerShell | Microsoft Learn 参考サイト2: Select-Object (Microsoft.PowerShell.Utility) – PowerShell | Microsoft Learn 参考サイト3: Format-Table (Microsoft.PowerShell.Utility) – PowerShell | Microsoft Learn
アバター
本記事は 新人ブログマラソン2024 の記事です 。 こんにちは。2024年度に入社した新人の曲渕です。 AWSの技術習得に向けて日々勉強している最中です。そんなAWSの知識があまりない私ですが、現在サーバーレスの案件に携わらせてもらっています。コンテナ周りのサービスは難しいイメージがあるので正直不安ですが、なんとかやり遂げられるよう頑張りたいです! 今回の記事ではその案件の中で AWS Systems Manager で AWS Fargate のコンテナにログインする機会があったので、その備忘録として記事にしました。 私と同じようにAWSの勉強をされている方や来年入社される方の参考になれば幸いです。 構成図 今回やりたいこととしては、プライベートサブネットに配置されたECS クラスターのFargateのコンテナにSystems Managerでログインすることです。 実施手順の流れ 以下に今回行った手順の流れを記載します。 ①VPC、リポジトリ、IAMロールの作成 ②Dockerファイルを記述( コンテナの設計図をつくる ) ③リポジトリにイメージを格納( 作ったコンテナの設計図を保管 ) ④クラスターを作成( コンテナを動かすための作業場を用意 ) ⑤タスク定義( リポジトリに保管したコンテナの設計図を引っ張り出してリソースを割り当てて使えるようにする ) ⑥サービスの作成( どのタスク定義を使用してコンテナを実行するか ) ⑦Systems ManagerのセッションマネージャーでFargateのコンテナにログイン VPC、リポジトリ、IAMロールの作成 VPCは10.0.0.0/16のCIDRブロックのものを使用し、NAT gatewayをパブリックサブネットに一つ配置します。 リポジトリはECRから作成します。作成が完了するとURIが表示されますが、このURIは後ほど使用します。画像の赤枠の部分はリポジトリの名前を入力します。 IAMロールはSystems ManagerでFargateのコンテナにログインできるように以下のものを設定しました。画像の備考にあるecsTaskExcutionRoleのIAMポリシーは既存のもので、下のtest-magarifuchi-roleのIAMポリシーはJSONファイルから記述しています。赤文字で アカウントID とある部分にはAWSアカウントのIDを記述します。test-magarifuchi-roleはタスクロール、ecsTaskExecutionRoleはタスク実行ロールで使用します。 Dockerファイルを記述 Cloud9を使用して以下のコマンドを順に実行しました。赤文字の部分はそれぞれAWSアカウントのIDと作成したリポジトリのURI、使用しているリージョンを入力します。 ①mkdir my-image( 新しいフォルダを作成 ) ②cd my-image( 作成したフォルダに移動 ) ③nano Dockerfile( Dockerファイルの記述 ) ④touch entrypoint.sh( entrypointシェルスクリプトファイルを作成 ) ⑤nano entrypoint.sh( entrypointシェルスクリプトファイルを記述 ) ⑥docker build -t my-image:latest .( イメージをビルド ) ⑦docker tag my-image:latest リポジトリのURI :latest( イメージにタグ付け ) ⑧aws ecr get-login-password –region ap-northeast-1 | docker login –username AWS –password-stdin アカウントID .dkr.ecr. リージョン .amazonaws.com( ECRにログイン ) ⑨docker push リポジトリのURI :latest( イメージをリポジトリにプッシュ ) 以下に作成したDockerファイルを記載します。 このDockerファイルではPHP、Apache構成のコンテナを記述しています。緑の括弧で示した部分はSystems ManagerでログインできるようにSSMエージェントをインストールし、「#SSMエージェントとアクティベーション処理用のスクリプトを追加」の部分でコンテナが起動される際に実行されるentrypoint.shを指定しています。以下のファイルがentrypoint.shの内容です。 entrypoint.shでは「#コンテナのマネージドインスタンスへの登録」でSSMエージェントを使用して、FargateコンテナをSystems Managerに登録しています。これでコンテナがSystems Managerによって管理されるようになります。 リポジトリにイメージを格納 手順⑨を実行するとリポジトリにイメージが格納されるはずです。実際にリポジトリの中身を見てみるとイメージがあることが確認できました。 クラスターを作成 クラスターの作成では名前は任意のものを設定し、Fargateを選択します。 タスク定義 タスク定義では、先ほど格納したリポジトリのイメージURIからコンテナの詳細な設定を行います。タスク定義ファミリーは任意の名前を入力し、起動タイプはFargate、ロールは冒頭で作成したIAMロールを設定します。黒く塗りつぶしているところはアカウントIDになります。 サービスの作成 作成したクラスターに移動してサービスの作成を行います。先ほど作成したタスク定義を選択し、サービス名を任意の名前で入力します。VPCの選択では構成図に従ってプライベートサブネットを選択します。セキュリティグループは新しく作っていますが、既存のものから選択することもできます。また、オートスケーリングの設定やロードバランサーの選択などもサービスの作成で行えますが、今回は赤枠以外のところはすべてデフォルトのままです。 クラスターの「タスク」タブのタスクを選ぶと、以下の画像のようにコンテナが実行されていることが確認できます。 Systems ManagerのセッションマネージャーでFargateのコンテナにログイン 実際にSystems Managerのセッションマネージャーに移動すると、Fargateのコンテナが表示されていることが確認できました。 表示されたターゲットインスタンスを選択し、「Start session」を選ぶと以下の画面のようになります。コマンドを打つと応答が返ってきたことからログインが成功しているのが確認できました。 おわりに 今回はSystems ManagerでFatgateのコンテナにログインしましたが、調べてみるとECS Execというものを利用してコンテナにログインする方法もあるようです。機会があれがそちらも試してみようと思います。 最後まで読んでいただきありがとうございました! 参考サイト
アバター
こんにちは、SCSKの木澤です。 去る2024/12/2~12/6に米国ラスベガスで開催されたAWS最大のカンファレンス、 re:Invent 2024に参加してきました 。 今回は私が現地で参加したセッションで感じたことや主観をまとめたいと思います。 概要 re:Inventは AWSによるクラウドコンピューティングに関する世界最大規模の「学習型」カンファレンス で、というくだりは一昨年にも書いたので詳細は省略したいと思います。 AWS re:Invent 2022レポート ~概要編~ 去る2022/11/28~12/2に 米国ラスベガスで開催されたAWS最大のカンファレンス、re:Invent 2022に参加してきました。 本記事では参加体験の概要をレポートとしてまとめたいと思います。 blog.usize-tech.com 2022.12.12 今年は世界中からの 現地参加者60,000人 (オンライン視聴者400,000人)、 日本からの現地参加者も1,700名超 で日本人の方も多く見かけました。ラスベガス市内 6つの会場で約3,500人から提供される約2,000のセッションが提供されており… といっても規模感のイメージは難しいかもしれません。 私は2022から連続して3回目の参加となりますが、 初回参加(2022)の際は とにかく圧倒されてしまった といった印象 で、2回目からやっと自分のペースで参加できています。 今年の大きな変更は、会期が1週間後ろ倒しになったくらいで、あとは会場レイアウトの変更くらいであったかと思います。 (Badge PickupとSWAGが別部屋になった、Funブースがマンダレイベイからシーザーズフォーラムに変更になった等)   参加したセッションについて 私は今年、業務や資格取得などでre:Inventに気持ちを切り替えるのが遅れ、セッション予約が後手に回ってしまいました。 (参加者が増えた影響からか、今年は例年以上に人気セッションが埋まるのが早かった印象です) そのため、今年はキーノートやワークショップ、新サービスに関するブレイクアウトセッションを中心に組み立てました。 特にワークショップを5件と多めに入れました。 今回は受講したセッション・ワークショップをかいつまんでご紹介したいと思います。 [SEC235-NEW] Accelerate security analytics across hybrid environments with AWS Amazon Security LakeがAmazon OpenSearch ServiceとのZero-ETL統合ができるようになった よ、というアップデートです。 Amazon Security Lakeは昨年のre:Inventで発表されたものの、ツール側の対応途上であることからか、まだ導入事例は多くないと感じます。各種セキュリティ製品のログ・分析の一元化に繋がるので期待したいですね。 SecurityLake側でサブスクライバを作成した後、OpenSearch側でデータソースとして指定するだけで、自動的にでダッシュボード及び関連クエリーまで作成されます。 ダッシュボードの利用デモンストレーションもありました。格好良い見た目ですね。 ぜひ時間を作って触ってみたいと思います。 [NET217-NEW] Accelerate data transfer to the cloud with AWS Data Transfer Terminal 新サービスであるAmazon Data Transfer Terminal に関するセッション。 なんとこれは、AWSが Direct Connect(100G)に繋がる部屋を貸してくれる 、というサービスです。 マイグレーションやデータ解析等で大容量のデータをAWSに移行する場合、AWS Snowball Edge等が従来から提供されていましたが、現在Snowballは注文しても大幅に納期が掛かる状況になっていると聞いています。(※最新の状況はAWSにお問い合わせください) こういった状況もあるので、新しいサービスを作ったという背景のようです。 接続としては、Direct Connect Gatewayに直結するイメージとなります。 利用申請(予約)はマネジメントコンソール上から行い、その後メールで通知が届きます。 予約日に機器を持ち込み、自身のAWSアカウントのクレデンシャルを用いてS3等にアップロードとの流れになるようです。 部屋はこんな感じ、いや本当でしょうか?w まずは米国(オレゴン)のリージョンからGAとなり、東京リージョンは「ポテンシャル枠」との表現でした。 ただし、プレビューを利用した日本企業の方のコメントも掲載されていたため、可能性としてはあるかもしれません。 [SEC306] Securing your generative AI applications on AWS Amazon Bedrockの機能を用いて、生成AIの応答を制御する方法を学ぶワークショップでした。 ワークショップでは、医療現場にて患者のカルテのデータから情報を返答するチャットボットアプリを例に設定を行いました。 医師と受付のスタッフが利用しており、医師のみに血液型等の機微な情報を開示するよう設定してあるのですが、受付スタッフにおいても「急ぎ」などとプロンプトを工夫することで患者の機微な情報にアクセスできてしまうという脆弱性があります。 Amazon Bedrockのガードレール機能やデータソースの権限分離を行うことで不正アクセスから防御する手法を理解できました。 解像度を高めるため、機会があればまた受講したいですね。 以下、適宜追加します   全般的な感想 基調講演(KeyNote) CEOがMatt Garmanに代わったということで注目されたDay2のKeyNoteですが、冒頭にAWS Heroを含むコミュニティへの感謝から入ったのは印象的でした。 AWSは古くから支持する熱狂的なファンに支えられてここまで来たものと思いますし、CEOが新しくなってもそれらコミュニティへのコミットメントは変わらないという意思を示したのかと私は考えています。 また、メッセージングは全般的にアグレッシブであった印象を持ちました。 他社との協調についても控えめで、Amazon Novaなど新機能リリースで攻めの姿勢であったように感じます。 Amazon Nova Amazon Novaの発表では初代CEOのAndy Jassyが登壇し会場を沸かせ、Amazon Novaへの並々ならぬ意気込みを感じました。 その他アップデート 他にもMattの基調講演では以下のようなアップデートの発表がありました。 今後もユーザーニーズに応じて全方位的にサービスを提供していくということなのでしょう。 AWS Trainium3 AI用プロセッサ。学習用⇒推論にも利用可能 Amazon S3 Tables Apache Iceberg互換の分析ワークロードのために最適化されたストレージ Amazon Aurora DSQL マルチリージョンの分散データベース Amazon Bedrockの進化 Model Distillation(LLMモデルの”蒸留”⇒小型高速化) Automated Reasoning checks(自動検証) multi-agent collaboration(複数エージェントの連携機能) Amazon SageMakerのリブランディングとAmazon Qの進化 Amazon SageMakerのリブランディングによってデータ活用の範囲を取り込むとの発表もありました。 また、Amazon Qの新機能として.NETアプリケーション / VMware環境 からのマイグレーションサポート機能が発表されました。 つまりAWSの方向性としては、あらゆるユーザー支援機能をAmazonQに統合する方向性=「Amazon Qが全てを包含する」世界を目指しているのだと理解しました。将来的には今後はAWSアカウント上にある、あらゆる情報 AWSアカウントの利用状況 各サービスの設定 マイグレーションの支援 ユーザーデータ これらをAmazon Qにて問い合わせできるような世界観になるのでしょう。 また、今までデータ活用にはDWHを構成するにあたり少々ノウハウが必要でしたが、データ分析とのAnalyticsの融合により、AWSに集まったデータを気軽にAIで分析する世界を実現しようとしているのはワクワクするなと思いました。   その他現地での体験 AWS Partner Award 2024 – Industry Partner of the Year ‐ Retail, APJ 受賞 今回、SCSKはAWS Partner Award にて Industry Partner of the Year ‐ Retail, APJ  の表彰を頂戴することができました。 Announcing the 2024 Geo and Global AWS Partners of the Year | Amazon Web Services Each year, we honor members of the AWS Partner Network (APN) from around the globe that play key roles in helping custom... aws.amazon.com 弊社のお知らせ にも記載しておりますが、当社では、住友商事をはじめ多数のお客様でのAWSクラウド活用及びモダナイゼーションの推進を行っており、本表彰はこれら弊社の実績を評価いただいたものとなります。 なお私も表彰式後のパーティにご招待いただき、滅多にない経験をすることができました。 会場の雰囲気はAWS公式の動画がわかりやすいと思いますので、こちらをご覧下さい。 現地でのボランティア、交流、おまけ 今回、初の試みとしてAPNブースのボランティアスタッフを担当しました(30分のみですが) 長い時間はご一緒できませんでしたが、AWS Ambassadorsの皆様との交流も楽しめました。   最後に こうして振り返ってみると、やはり今年はセッション予約が後手に回ったことが失敗だったなと反省ですね。 それでも、今年もre:InventはAWSに関わる人々にとって刺激となるイベントであり、私も期間中ずっとハイテンションとなっておりました。(レポートが遅れました) なお、参加者向けのアドバイスを1点付け加えるとすれば(他の方のレポート同様となりますが)やはり現地でしか体験できないプログラム、WorkShopやGameDay、Fun Event(re:play、etc)を優先した方が良いかと私も思います。 また、アウトプットを予定しているのであればそれを踏まえた行動計画を立てると良いでしょう。 この記事が来年訪問される皆様のきっかけになれば幸いです。 ありがとうございました。 re:Invent2024振り返りウェビナーのご紹介 2025/1/20に、トレノケート株式会社と共同でre:Invent2024の振り返りイベントを開催いたします。 私も登壇いたします。 新時代のクラウド活用方法を語る ~AWSプレミアパートナーと共創する、自社のクラウド活用力の高め方~ 2024年12月に米国ラスベガスで開催されるAWS最大のグローバルイベント「AWS re:Invent 2024」を振り返りイベントを開催いたします。クラウドを効果的に活用するために自社が持つべき知見、インテグレーターとの協力の進め方、そし... www.scsk.jp ぜひご参加頂ければと思います。よろしくお願いします。
アバター
本記事は TechHarmony Advent Calendar 2024 12/25付の記事です 。 こんにちは、SCSKでAWSの内製化支援『 テクニカルエスコートサービス 』を担当している貝塚です。 re:Invent 2024で『VPC LatticeがTCPによるアクセスをサポート』というアナウンスがありました。 VPC Lattice now includes TCP support with VPC Resources - AWS Discover more about what's new at AWS with VPC Lattice now includes TCP support with VPC Resources aws.amazon.com PrivateLinkの機能追加 とあわせて、AWSのネットワークまわりに大きな変化がありそうです。 ……と言っておきながら、実は私、VPC Latticeを使ったことがありません。というのも、VPC Latticeはコンテナと合わせて語られることが多く、あまりコンテナと関わってこなかった私にとっては、そのうち勉強しようと思っているけれどなかなかその機会に恵まれないサービスだったのです。 そういうわけで、re:Inventで発表された機能を使ってみる前に、とりあえずVPC Latticeを使ってみよう、というのが本記事の趣旨になります。 検証構成 今回は、まずは基本を抑えねばということで、新機能の検証ではなく、従来通りのVPC Latticeを試してみることにしました。図にすると以下のようになります。 VPC Latticeがどういうものかというと、ALBに近いものをイメージすればよさそうです。今回の機能拡張前は、HTTPとHTTPSのみの対応であったという点もALBに似ています。 Service NetworkがALB本体のようなもので、接続元となるVPCに関連付ける必要があります。 Serviceは、ALBのリスナーとターゲットグループをひとまとめにしたようなものです。リスナーとターゲットグループという用語はALBそのままですね。何番ポートで通信を待ち受けて(リスナー)、どこ(どのIP/どのインスタンス、など)に転送するか(ターゲットグループ)を設定します。 環境構築 マネジメントコンソールでは、『VPC』→ 『PrivateLink and Lattice』のメニューから設定を進めていきます。 ターゲットグループの作成 まずは接続先を定義するターゲットグループから作成していきます。 メニューから『ターゲットグループ』を選択し、『ターゲットグループの作成』をクリックします。 ターゲットタイプの選択では、インスタンスよりIPアドレスの方が汎用性が高い使い方ができるだろうということで、IPアドレスを選択しました。 ターゲットグループ名、プロトコル、ポート、IPアドレスタイプ、接続先側のVPC、プロトコルバージョン、ヘルスチェックを指定します。今回はつながりさえすればよいというのを目標にしているので、HTTP(80番ポート)でEC2に接続し、ヘルスチェックもデフォルトのまま( / へのHTTPによるヘルスチェック)にします。 次の画面に進むと、ターゲットの登録をすることになります。VPCを選択し、接続先のIPとポートを指定して、『保留中として以下を含める』をクリックするとターゲットとして登録されます。 ターゲットが登録されたら『ターゲットグループの作成』をクリックしてターゲットグループの作成を完了させます。 サービスの作成 メニューから『Lattice services』を選択し、『サービスを作成』をクリックします。とりあえずつながることを目指しているので、カスタムドメイン設定、サービスアクセス、Share service、モニタリングはデフォルトのまま進みます。 次へ進み、『ルーティングを定義』というところでリスナーの設定をします。プロトコルをHTTPに、ポートを80に設定します。リスナールールは設定せず、リスナーのデフォルトアクションとして『ターゲットグループへ転送します』を選び、ターゲットグループとして先ほど作成したターゲットグループを選択します。 次にサービスネットワークの関連付けの画面が出ますが、まだサービスネットワークは作成していないので、飛ばします。『確認と作成』画面が出るので、『VPC Latticeサービスを作成』をクリックしてサービスを作成します。 サービスネットワークの作成 メニューから『サービスネットワーク』を選択し、『サービスネットワークを作成』をクリックします。 サービスの関連付けのところで、先ほど作成したサービスを選択します。VPCの関連付けのところで、接続元のVPCを指定し、適用するセキュリティグループを選びます。他はデフォルトのままでサービスネットワークを作成します。 接続確認 設定はこれで完了ですが、先ほど登録したターゲットは生きていると判定されているでしょうか。作成したターゲットグループを選び、『ターゲット』タブを確認します。ヘルスステータスが正常になっているので、うまくいったようです。 さて、接続確認を実施したいのですが、どこにアクセスすればよいのでしょうか?答えは、「サービス」の詳細のところにあります。 「ドメイン名」を確認し、接続元VPCのEC2から接続してみます。 接続できました! 考察 接続できましたが、どのような仕組みになっているのか気になるところです。 まずは、ドメイン名を名前解決するとどうなるか確認してみます。 169.254. から始まるリンク・ローカル・アドレスが返ってきました。 接続元EC2が存在するサブネットのルートテーブルを確認してみます。 129.224.0.0/17と169.254.171.0/24が登録され、ターゲットがVpcLatticeとなっています。 129.224.0.0/17はどこで使われるのでしょうね?これは今後気が向いたら調べることにします。 次に、接続を受ける側のEC2で、どこから通信が来ているのかtcpdumpを使って確認してみます。 / にはいくつかのIPから通信が来ていて紛らわしいので、/test にアクセスしてみました。169.254.171.194から来ていることが分かります。こちらもリンク・ローカル・アドレスということですね。 接続先側のルートテーブルも確認してみます。 169.254.171.0/24が登録され、ターゲットがVpcLatticeとなっています。 今回、説明を端折りましたが、169.254.から通信が来るので、接続先EC2のセキュリティグループで169.254.のCIDRを許可してやる必要があります。 接続元側は169.254.xx.xxのIPに接続し、接続先側は169.254.xx.xxから通信が来ているように見えるということは、もし別のVPCから利用したいという場合には一工夫する必要がありそうです。もっとも、一工夫するのではなく素直に接続元VPCにLatticeを構築するのが正攻法だと思います。 まとめ VPC Latticeの新機能を試す前に、まずはこれまでのVPC Latticeがどういうものなのか知らなくては、という動機でこの記事を書き、なんとなくどういうものなのか分かってきました。 次は、新機能の検証に進みます。
アバター
SCSKの畑です。初投稿です。 現在携わっている複数の案件において、サーバレスアーキテクチャにおける WEB アプリケーション(フロントエンド/バックエンド双方)の構築・開発を行っています。これらの案件において、苦労した点や工夫した点などを中心に記載していく予定です。 アーキテクチャ概要 主にデータベース/ DWH 上のテーブルデータをメンテナンスする WEB アプリケーションを作成しています。案件ごとに差異はありますが、概ね以下のようなアーキテクチャで開発を行っています。下図の場合は、メンテナンス対象の DWH が Redshift となっています。 正直なところ、AWS 上におけるサーバレスアーキテクチャとしては王道というか、特に珍しくない構成だと思いますが。。。 本題 さて、アプリケーション開発の本筋から少し離れたトピックとなるのですが、実際に構築・開発を行うAWS環境がどのように管理・運用されているかというのは案件ごとに異なるところだと思います。今回、お客さん側の組織で主体的に管理されている1つの AWS アカウント環境上で構築する必要があり、使用するサービスの種類、必要なリソースの個数や IAM 権限などについてもある程度明確にした上で申請し、お客さん側に作成等を対応頂く流れとなっていました。また、各種 AWS サービスやリソースの構築・実装についても、 AWS CLI 等は使用できず、全てAWS マネジメントコンソール上から実施する必要がありました。 もちろん、全てをお客さん側で対応頂くというのは色々な観点から現実的ではないので、例えば Lamdba の場合は関数の作成までをお客さん側に実施頂き、ソースコード実装を含む詳細な設定を我々で実施するような棲み分けで権限管理がなされていました。他サービスについても概ね似たような棲み分けで構築を進めることができたのですが、AWS AppSync については一筋縄ではいきませんでした。 ご存じの通り、AWS AppSync における構成単位は API となり、その配下に同 API を構成する各種リソース(スキーマ、データソース、関数など)が紐付いています。つまり、先述したような権限管理の考え方に則ると、APIまでをお客さん側に作成してもらい、配下のリソースについては我々で編集できるような IAM 権限を付与頂くという棲み分けが妥当と考え、お客さんにそのような依頼をしたところ・・ なんと NG をもらってしまいました。詳細を確認した所、対象の IAM 権限( appsync:StartSchemaCreation など)がリソースタイプの指定に対応しておらず、IAM 権限を付与した場合他の API 配下のリソースについても編集可能となってしまうためとのことでした。 なお、本案件についてはお客さんの AWS 環境で Amplify を使用することがポリシー上 NG であったため、自社環境において Amplify を使用して開発したものをお客さん環境に持ち込む、という段取りで進めていました。 そんなバカなことある!?と慌ててドキュメントを確認したところ、確かにそのような記載になっています。(表内のリソースタイプ列に記載がないため、リソースタイプでの許可がサポートされていない) AWS AppSync のアクション、リソース、および条件キー - サービス認可リファレンス Word へのアクセスを制御するために IAM ポリシーで使用できるサービス固有のリソースやアクション、条件キーを一覧表示します AWS AppSync。 docs.aws.amazon.com 念のため原著(US版)も確認したのですが同じ内容。うーん、万事休すか。。。 Actions, resources, and condition keys for AWS AppSync - Service Authorization Reference Lists all of the available service-specific resources, actions, and condition keys that can be used in IAM policies to c... docs.aws.amazon.com よって、作成したい各種リソースの詳細な内容をお客さんに伝えて作成頂くように・・ と、普通なら引き下がるのですが今回は易々と引き下がれない事情がありました。理由は明白で、API については言うまでもなくアプリケーションの開発領域であるためです。 つまり、アプリケーションのリリースが完了するまでの開発期間はもちろん、リリース後の機能追加なり不具合修正も含めて、API 配下のリソースに変更がある場合はその反映をお客さんにお願いしないといけない、ということになってしまいます。対象となるスキーマのパイプラインや関数の個数も多く、これをそのままお客さんにやってもらう場合にどれくらいの時間・コストが必要かを考えると、いや無理でしょうという結論になりました。 そもそも API の実行( appsync:GraphQL )についてはちゃんとリソース指定できるようになっているのに、編集はできないというのはどういうことなのか? AWS の各種サービスにおける権限管理があれだけしっかりとした設計思想の元で作られているのに、API という単位で明確に分割できる AWS AppSync についてそのようなことができないような設計になっているとは思い難い などと悶々とした結果、自分で試してみないと納得できなくなったので、手元の環境で試してみました。 検証 AWS マネジメントコンソールから AWS AppSync の API 配下のリソースを編集できる IAM ポリシーを、IAM ユーザに付与できるかどうか、というのが検証の内容となります。対象の IAM 権限は複数ありますが、一番分かりやすいところでまずはスキーマを編集できる権限( appsync:StartSchemaCreation)について検証してみることに。 以下検証においてはスクリーンショットに表示されている以外の IAM 権限エラーも複数表示されていましたが、検証目的から外れる内容のものは省略しています。ご了承ください。 まず、確認用の AWS AppSync API 「AppSync-API-test」を作成した上で、スキーマ編集画面をエラーなく開くための以下 IAM ポリシーを付与した IAM ユーザで、AWS マネジメントコンソールからスキーマの編集画面を開いてみます。  ちなみに、「appsync:ListSourceApiAssociations」についてもドキュメント上はリソース指定に対応していませんでしたが、「 appsync:GraphQL」と同じような ARN パスでリソース指定ができたので、他の権限についてもできるのではなかろうかという楽観的予測が芽生えつつありました。 { "Version": "2012-10-17", "Statement": [ { "Sid": "1", "Effect": "Allow", "Action": [ "appsync:ListGraphqlApis" ], "Resource": "*" }, { "Sid": "2", "Effect": "Allow", "Action": [ "appsync:ListSourceApiAssociations" ], "Resource": "arn:aws:appsync:${Region}:${Account}:apis/${GraphQLAPIId}/*" } ] } もちろんこの時点では「ロード中」の文字がフォーム内に残ったままになっており権限不足なことは明白ですが、この状態から適当なスキーマ定義を入力してみると、 このように「appsync:ListResolvers」権限に関するエラーが発生。最終的にはこの権限も必要になるでしょうが、一旦スルーしてスキーマを保存してみます。 すると、狙い通りに「 appsync:StartSchemaCreation 」権限に関するエラーが発生したので、こちらに対してリソース設定をしてみようということでエラーメッセージの内容を見たところ、リソースの ARN パスが「appsync:ListSourceApiAssociations」のものと少し異なることが判明。よく見ると 「appsync:ListResolvers」も同じようなARNパスになっており、以下の通り「 /v1/ 」が追加されているのが分かります。 arn:aws:appsync:${Region}:${Account}:apis/${GraphQLAPIId}/<対象リソース名> arn:aws:appsync:${Region}:${Account}: /v1/ apis/${GraphQLAPIId}/<対象リソース名> しかもこの「 /v1/ 」が入ったリソースの ARN パス、ドキュメントに記載がないんですよね・・ Actions, resources, and condition keys for AWS AppSync - Service Authorization Reference Lists all of the available service-specific resources, actions, and condition keys that can be used in IAM policies to c... docs.aws.amazon.com ということで、ちょっときな臭い感じがするものの、普通にこの ARN パスをそのまま設定すれば良いのではないか?と推測して以下のようにポリシーを変更してリトライ。「appsync:GetSchemaCreationStatus」についても必要そうだったので合わせて追加しています。 { "Version": "2012-10-17", "Statement": [ { "Sid": "1", "Effect": "Allow", "Action": [ "appsync:ListGraphqlApis" ], "Resource": "*" }, { "Sid": "2", "Effect": "Allow", "Action": [ "appsync:ListSourceApiAssociations" ], "Resource": "arn:aws:appsync:${Region}:${Account}:apis/${GraphQLAPIId}/*" }, { "Sid": "3", "Effect": "Allow", "Action": [ " appsync:StartSchemaCreation ", "appsync:GetSchemaCreationStatus" ], "Resource": "arn:aws:appsync:${Region}:${Account}:/v1/apis/${GraphQLAPIId}/*" } ] } すると、エラーが出力されずにスキーマの保存が完了しました!! 後は必要となりそうな各種 IAM 権限を調べてお客さんに伝えてクローズ!と進めたいところなのですが、ドキュメントに記載されていない内容であるため AWS サポートに裏取り。マニュアルに記載されていない以上は想定されない挙動(≒不具合)の可能性もあり、そのような結論にならないことを願っていたのですが・・ AppSync の StartSchemaCreation アクション等でリソースレベルのアクセス制御が可能となっている事象につきまして、想定された動作であることを確認いたしました。 そのため、ご連絡いただきましたようなポリシーにて特定の AppSync API のみスキーマ、リゾルバ、関数を更新いただくことが可能でございます。 ドキュメントやビジュアルエディタ等に本動作が反映されていない点につきましては、製品担当部署にフィードバックを実施いたしました。 ドキュメントについては、本日時点ではまだ反映されていないようでした。 上記回答の通り、無事に想定された動作であることを確認できたので、改めて上記内容及び追加設定頂きたい IAM ポリシーをお客さんに伝えてクローズと相成りました。最後に、実環境に設定した IAM ポリシーを記載しておきます。 データソースについてはスキーマやパイプライン、関数などと比較して変更のタイミングが限定的であるため、権限付与しない方針としています。(今考えると付与してしまっても良かった気はしますが・・) AWS マネジメントコンソールからクエリを実行して動作確認を行うことがあるので、それができるように「 appsync:GraphQL 」権限を追加しています。 { "Version": "2012-10-17", "Statement": [ { "Sid": "1", "Effect": "Allow", "Action": [ "appsync:ListGraphqlApis" ], "Resource": "*" }, { "Sid": "2", "Effect": "Allow", "Action": [ "appsync:GraphQL", "appsync:ListSourceApiAssociations" ], "Resource": [ "arn:aws:appsync:${Region}:${Account}:apis/${GraphQLAPIId}/*" ] }, { "Sid": "3", "Effect": "Allow", "Action": [ "appsync:GetSchemaCreationStatus", "appsync:GetResolver", "appsync:GetFunction", "appsync:GetApiCache", "appsync:ListFunctions", "appsync:ListResolvers", "appsync:ListTypes", "appsync:ListDataSources", "appsync:ListResolversByFunction", "appsync:StartSchemaCreation", "appsync:CreateResolver", "appsync:UpdateResolver", "appsync:DeleteResolver", "appsync:CreateFunction", "appsync:UpdateFunction", "appsync:DeleteFunction", "appsync:ListApiKeys" ], "Resource": [ "arn:aws:appsync:${Region}:${Account}:/v1/apis/${GraphQLAPIId}/*" ] } ] } まとめ ドキュメントなどの公式情報の内容を抑えておくというのはもちろんですが、実環境での検証も同じくらい大事だということを改めて実感できました。私自身はむしろ後者が先行しがちなのですが、バランスが大事ですね。 本記事のようなシチュエーションに遭遇する機会はそれほど多くないと想像しますが、どなたかの役に立てば幸いです。
アバター