TECH PLAY

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

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

1226

こんにちは、広野です。 AWS AppSync はレスポンスが速くてサブスクリプションを手軽に作れて便利なのですが、ネイティブに CORS 対応はしていません。CORS が必要になった場合には、現時点では Amazon CloudFront をかぶせて CORS ヘッダーをオーバーライドするのが一番スマートかな、と思います。 と、思ってたら簡単には行かなかったので、気付いたことを残しておきます。 実装したアーキテクチャ 冒頭で説明したように、AWS AppSync はネイティブに CORS をサポートしていません。レスポンスで返ってくる Access-Control-Allow-Origin ヘッダーは “*” 固定になっています。そのため、これを Amazon CloudFront distribution でレスポンスヘッダーを上書きします。 CORS は https:// から始まる宛先への通信をサポートしています。AWS AppSync への Query は https の POST メソッドを使用しているようで、Amazon CloudFront を介して CORS を有効化させられます。ところが Subscription (WebSocket) は wss:// から始まる宛先になるので、ブラウザは CORS をサポートしていません。一般的に、WebSocket で同様のセキュリティ対策をしようと思ったら Origin ヘッダーのチェックをするようです。ということなので、Subscription 通信については Amazon CloudFront を通さずに行きます。他にもそのようにした理由がありますが、細かすぎるので最後の方で補足します。 さて、Query の CORS 有効化に話を戻します。レスポンスヘッダーを上書きするだけじゃん?と思うのですが、今回の構成では AWS AppSync が Amazon Cognito 認証になっています。その場合、リクエストの Authorization ヘッダーに Amazon Cognito から受け取ったトークンを格納して送りますので、Amazon CloudFront が AWS AppSync にそれを転送する必要があります。 ここで気を付けないといけない点があり、Authorization ヘッダーをオリジン (ここでは AWS AppSync) に転送するには、特定の設定方法でしか実装できません。それが以下の AWS ドキュメントに書いてあります。 オリジンリクエストにカスタムヘッダーを追加する - Amazon CloudFront CloudFront からオリジンに送信するリクエストにカスタムヘッダーを追加します。 docs.aws.amazon.com まるっとビューワーヘッダー全体指定で転送するか、キャッシュポリシーに Authorization を明記するか、の大まかに 2 択です。私は今回のケースでは Amazon CloudFront にキャッシュをさせたくなかった (通信をパススルーさせたい) ので、キャッシュポリシーをマネージドの CachingDisabled を選択することにしていました。そのため、まるっとビューワーヘッダー全体指定の転送を選択しています。ただし、それだけでは転送されず、レスポンスヘッダーのオーバーライドで使用予定だった Access-Control-Allow-Headers に Authorization を追加すると転送されるようになりました。 今回の一番の目的である CORS 有効化ですが、この設定自体は簡単です。具体的には後述の設定の章を見て欲しいですが、レスポンスヘッダーポリシーに CORS 用設定があるので、そこに CORS 用のヘッダー情報を入れるだけです。また、オーバーライドする設定を有効にします。ここよりも、他のヘッダー設定の方が苦労しました。 ポリシーを使用して CloudFront レスポンスの HTTP ヘッダーを追加または削除する - Amazon CloudFront レスポンスヘッダーポリシーを使用して、Amazon CloudFront が HTTP レスポンスで追加または削除する HTTP ヘッダーを指定します。 docs.aws.amazon.com   React アプリ側は、以下のコードで AWS 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: { // AppSync の標準のエンドポイント endpoint: 'https://example1234567890000.appsync-api.ap-northeast-1.amazonaws.com/graphql', region: 'ap-northeast-1' defaultAuthMode: 'userPool', // 独自ドメインの CloudFront に置き換えたエンドポイント customEndpoint: 'https://xxx.hironoenterprise.com/graphql', customEndpointRegion: 'ap-northeast-1' } } } ); CORS 有効化のため AWS AppSync に手作りカスタムドメインを追加構築したようなものなので、カスタムドメインの設定を追加したところ Query 通信にはカスタムドメイン (Amazon CloudFront のエンドポイント) に、Subscription 通信は AWS AppSync のエンドポイントにアクセスするようになりました。 Configure Amplify categories - JavaScript - AWS Amplify Gen 1 Documentation Configuring the client. AWS Amplify Documentation docs.amplify.aws   さて、、、ここまでで CORS 有効化設定そのものは整いましたが、周辺のアーキテクチャについても次章で説明します。 追加のアーキテクチャ もう 1 回同じ図を再掲します。 AWS AppSync への Query 通信を CORS 有効化できただけでも 1 つの進歩なのですが、まだ少々懸念が残っています。 AWS AppSync に Amazon CloudFront をかぶせただけでは、AWS AppSync にダイレクトにアクセスできる経路が残っている。 つまり、Amazon S3 で言う OAC (Origin Access Control) のようなことをした方が良い。 WebSocket 通信については、CORS 同等の対策ができていない。アクセス元アプリを、ここでは hironoenterprise.com に限定するような設定が必要。Origin ヘッダーをチェックさせたい。 これらについて、完璧ではないですが今時点できることを実装してみます。 AWS AppSync エンドポイントへのダイレクトアクセス拒否 これについては、AWS ブログで方法が紹介されています。原始的と言っては失礼ですが、今できることを他の AWS サービスを駆使して組み上げた感じです。 How to enhance Amazon CloudFront origin security with AWS WAF and AWS Secrets Manager | Amazon Web Services Whether your web applications provide static or dynamic content, you can improve their performance, availability, and se... aws.amazon.com 私の図をベースに説明しますと。 Amazon CloudFront から AWS AppSync にリクエストを転送するときに、「私は許可された CloudFront ですよー」と証明するためのキーをカスタムヘッダーに追加します。 リクエストを受け取った AWS AppSync は、リクエストを AWS WAF にチェックしてもらいます。カスタムヘッダーの値が、あらかじめ口裏合わせしておいたキーと同じであれば、アクセスを許可します。 (今回の私の例には含めていませんが) Amazon CloudFront と AWS WAF に持たせる口裏合わせのキーは AWS Secrets Manager で定期的に自動ローテーションします。 なんちゃって OAC AppSync 版、って感じですが、十分な機能ですね。私は今回はバッドプラクティスですが、キーはベタに書きました。そこは参考にしないでください。 Subscription (WebSocket) 通信の Origin チェック WebSocket は特殊な通信で、今回、AWS WAF のログやブラウザの開発者コンソールを見て、通信の仕組みがよくわからないことがわかりました。目的の Origin チェックは簡単に実装できるのですが、前述の AppSync 通信ダイレクトアクセス拒否が意味を失います。長くなりますが、説明します。 まず、Origin のチェックは AWS WAF Web ACL で、Origin ヘッダーがここでは https://hironoenterprise.com であることをチェックすればよいです。ただし、それは以下のように条件分けする必要があります。 Query 通信はカスタムヘッダーをチェックする。 Subscription 通信は Origin ヘッダーをチェックする。 では、AWS WAF がどの情報で Query か Subscription かを識別するかです。 まず、私は host ヘッダーの利用を考えました。host は通信の宛先の FQDN で、今回のケースですと AWS AppSync のエンドポイントになります。実は AWS AppSync は Query と Subscription でエンドポイントが異なります。 AWS AppSync でのリアルタイム WebSocket クライアントの構築 - AWS AppSync AWS AppSync リアルタイム WebSocket クライアント設定 docs.aws.amazon.com Query 用のエンドポイント (GraphQL エンドポイント) 例:https://example1234567890000.appsync-api.us-east-1.amazonaws.com/graphql SubScription 用のエンドポイント (リアルタイムエンドポイント) 例:wss://example1234567890000.appsync-realtime-api.us-east-1.amazonaws.com/graphql FQDN が異なることが確認できると思います。そのため、AWS WAF に残る host ヘッダーが変わり、それぞれの通信を識別できるだろうと考えました。 結論から言いますが、実際には host 情報がまさかの同じ!だったので、識別できませんでした。 Query 通信も SubScription 通信ともに、AWS WAF のログを見る限り、どちらも GraphQL エンドポイントの FQDN が host ヘッダーに残っていました。それ以外の情報では通信を絶対というレベルで識別できるものは見つけられませんでした。 ログを見ていて、SubScription の通信の動きでわかったことは。 まず、wss:// から始まるリアルタイムエンドポイントに GET の通信をしに行く。 これが AWS WAF には残らない!(WAF を通過しない???) その後、GraphQL エンドポイントに POST の通信をしに行く。これは AWS WAF に残る。でもエンドポイントが Query と同じなので Subscription の通信なのかがわからない。 WebSocket のセッションが張れた後は、 AWS WAF に通信の記録は残らない。 Subscription 用にリアルタイムエンドポイントなるものが用意されてはいるものの、目に見えるのは GraphQL エンドポイントの情報のみなので、いかんともしがたいです。 しかしながら、2 の POST の通信がブロックされると WebSocket セッション確立が失敗するのと、Origin ヘッダーはあったので Origin チェックは意味を成します。 Origin チェックを Subscription 通信限定で行うことができず、AWS WAF を通る全通信を対象にするしかないため、ダイレクトアクセス拒否の設定は意味なくなります。ただし、Subscription 通信を全くしない構成であれば機能します。 さらに補足 他の方法として、CloudFront Functions や Lambda@Edge でヘッダーを書き換える方法も検討しましたが、セキュリティの根幹となるヘッダーを書き換えることはできない仕様だったのであきらめました。(そりゃそうだ、それができたら Amazon CloudFront 使って不正アクセスできるようになりますわw) AWS WAF のログは必ず残しましょう。通信がなぜブロックされたのか、また許可された正常な通信はどのようなヘッダー情報を持っているのか確認できるので。 そもそも Subscription (WebSocket) 通信を Amazon CloudFront 経由に統合しなかったのか?と思われる方もいらっしゃるかもしれません。試しましたが、私はできませんでした。おそらく前述した、WebSocket 通信がトリッキーなことが原因だと思います。wss:// から始まる通信、GET から始まり途中から POST に変わる、そしてエンドポイントも変わる、host は GraphQL エンドポイント、、、などなど、通信仕様が理解できず、Amazon CloudFront にヘッダー処理をうまく組み込めませんでした。AWS AppSync はネイティブにカスタムドメインはサポートしており、それを設定すると AWS 側で管理する Amazon CloudFront distribution が立ち上がります。当然その構成では機能するはずなので、勝手な想像ですが Lambda@Edge 等も活用して通信が正常に通るように作り込んでいるのだと思います。ただし CORS はできませんが。 最後になってしまいましたが気になる AWS AppSync のレスポンスは、Amazon CloudFront を介しても体感的には変わらなかったです。さすがエッジロケーション。どうでもいいですが、使用されたエッジロケーションの所在地を意味するようなヘッダーがあって、xx県xx市からのアクセスだとあそこに誘導されるんだー、って一人で感動してました。 思った以上にアーキテクチャ説明が長くなってしまいました。次章で設定情報を紹介します。 具体的な設定 (AWS CloudFormation テンプレート) すみません、AWS CloudFormation テンプレートで失礼します。インラインで補足をコメントします。 AWS AppSync の設定については説明のテーマではないので割愛します。 AWSTemplateFormatVersion: 2010-09-09 Description: The CloudFormation template that creates S3 Buckets, AppSync API with a Web acl, a CloudFront distribution with CORS and relevant IAM roles. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SubName: Type: String Description: System sub name that is used for all deployed resources. (e.g. prod or dev) Default: dev MaxLength: 10 MinLength: 1 DomainName: Type: String Description: Domain name for URL. xxxxx.xxx (e.g. hironoenterprise.com) Default: hironoenterprise.com MaxLength: 40 MinLength: 5 AllowedPattern: "[^\\s@]+\\.[^\\s@]+" CertificateId: Type: String Description: ACM certificate ID. CloudFront only supports ACM certificates in us-east-1 region. Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx MaxLength: 36 MinLength: 36 CloudFrontOriginVerificationKey: Type: String Description: The random string key that AppSync verifies it is sent from the exact CloudFront. !Bad Practice! Use Secrets Manager instead. Default: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX MaxLength: 256 MinLength: 8 LogRetentionDays: Type: Number Description: The retention period (days) for AWS WAF logs. Enter an integer between 35 to 540. Default: 365 MaxValue: 540 MinValue: 35 Resources: # ------------------------------------------------------------# # S3 # ------------------------------------------------------------# S3BucketWafLogs: Type: AWS::S3::Bucket Properties: BucketName: !Sub aws-waf-logs-hironoenterprise-${SubName} LifecycleConfiguration: Rules: - Id: AutoDelete Status: Enabled ExpirationInDays: !Ref LogRetentionDays OwnershipControls: Rules: - ObjectOwnership: BucketOwnerEnforced PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true Tags: - Key: Cost Value: !Sub hironoenterprise-${SubName} # ------------------------------------------------------------# # AppSync 大部分を省略、Cognito の設定は外部テンプレートを参照しているのでこのままでは動きません # ------------------------------------------------------------# AppSyncApi: Type: AWS::AppSync::GraphQLApi Description: AppSync API for hironoenterprise Properties: Name: !Sub hironoenterprise-${SubName} AuthenticationType: AMAZON_COGNITO_USER_POOLS AdditionalAuthenticationProviders: - AuthenticationType: AWS_IAM UserPoolConfig: UserPoolId: Fn::ImportValue: !Sub CognitoUserPoolID-hironoenterprise-${SubName} AwsRegion: !Sub ${AWS::Region} DefaultAction: "ALLOW" IntrospectionConfig: DISABLED LogConfig: CloudWatchLogsRoleArn: !GetAtt AppSyncCloudWatchLogsPushRole.Arn ExcludeVerboseContent: true FieldLogLevel: ALL Visibility: GLOBAL XrayEnabled: true Tags: - Key: Cost Value: !Sub hironoenterprise-${SubName} DependsOn: - AppSyncCloudWatchLogsPushRole # ------------------------------------------------------------# # AppSync CloudWatch Invocation Role (IAM) # ------------------------------------------------------------# AppSyncCloudWatchLogsPushRole: Type: AWS::IAM::Role Properties: RoleName: !Sub hironoenterprise-AppSyncCloudWatchLogsPushRole-${SubName} Description: This role allows AppSync to push logs to CloudWatch Logs. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - appsync.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs # ------------------------------------------------------------# # WAF Web Acl for AppSync # ------------------------------------------------------------# WebAclAppSync: Type: AWS::WAFv2::WebACL Properties: Name: !Sub hironoenterprise-${SubName}-appsync Description: !Sub WAFv2 WebACL for AppSync hironoenterprise-${SubName} Scope: REGIONAL DefaultAction: Block: {} Rules: # Query 通信はカスタムヘッダーが正しく入っているかチェックする、ただし Origin チェックを併用すると意味はない - Name: !Sub AllowAppSyncGraphqlEndpoint-hironoenterprise-${SubName} Priority: 1 Action: Allow: {} Statement: AndStatement: Statements: - ByteMatchStatement: FieldToMatch: SingleHeader: Name: host PositionalConstraint: EXACTLY # GraphQL エンドポイント宛ての通信であることをチェック SearchString: !GetAtt AppSyncApi.GraphQLDns TextTransformations: - Priority: 0 Type: NONE # x-origin-verify というカスタムヘッダーをチェックする - ByteMatchStatement: FieldToMatch: SingleHeader: Name: x-origin-verify PositionalConstraint: EXACTLY SearchString: !Ref CloudFrontOriginVerificationKey TextTransformations: - Priority: 0 Type: NONE VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub AllowAppSyncGraphqlEndpoint-hironoenterprise-${SubName} SampledRequestsEnabled: true # Subscription 通信はという条件にしたかったが識別できず、全体に対してチェックしている - Name: !Sub AllowAppSyncRealtimeEndpoint-hironoenterprise-${SubName} Priority: 2 Action: Allow: {} Statement: AndStatement: Statements: - ByteMatchStatement: FieldToMatch: SingleHeader: # React アプリから呼び出したことを条件にしている、無くてもよい Name: x-amz-user-agent PositionalConstraint: STARTS_WITH SearchString: aws-amplify TextTransformations: - Priority: 0 Type: NONE - ByteMatchStatement: FieldToMatch: SingleHeader: # origin ヘッダーをチェックする、ここでは hironoenterprise.com であること Name: origin PositionalConstraint: EXACTLY SearchString: !Sub https://${DomainName} TextTransformations: - Priority: 0 Type: NONE VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub AllowAppSyncRealtimeEndpoint-hironoenterprise-${SubName} SampledRequestsEnabled: true VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub hironoenterprise-${SubName}-appsync SampledRequestsEnabled: true Tags: - Key: Cost Value: !Sub hironoenterprise-${SubName} DependsOn: - AppSyncApi WebACLAssociationAppSync: Type: AWS::WAFv2::WebACLAssociation Properties: ResourceArn: !GetAtt AppSyncApi.Arn WebACLArn: !GetAtt WebAclAppSync.Arn DependsOn: - WebAclAppSync WebAclLoggingConfigurationAppSync: Type: AWS::WAFv2::LoggingConfiguration Properties: ResourceArn: !GetAtt WebAclAppSync.Arn LogDestinationConfigs: - !GetAtt S3BucketWafLogs.Arn DependsOn: - S3BucketWafLogs - WebAclAppSync # ------------------------------------------------------------# # CloudFront ログの設定は外部テンプレートを参照しているのでこのままでは動かない # ------------------------------------------------------------# CloudFrontDistributionAppSync: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Enabled: true Comment: !Sub CloudFront distribution for hironoenterprise-${SubName}-appsync Aliases: - !Sub hironoenterprise-${SubName}-appsync.${DomainName} HttpVersion: http2 IPV6Enabled: true PriceClass: PriceClass_200 Logging: Bucket: Fn::ImportValue: !Sub hironoenterprise-S3BucketDomainName-Logs-${SubName} IncludeCookies: false Prefix: cloudfrontAccesslogAppsync/ DefaultCacheBehavior: TargetOriginId: !Sub AppSyncOrigin-hironoenterprise-${SubName}-https ViewerProtocolPolicy: redirect-to-https # 以下の AllowdMethods と CachedMethods の設定は書き方に制約があるのでこの通りに書く AllowedMethods: - GET - HEAD - OPTIONS - PUT - PATCH - POST - DELETE CachedMethods: - HEAD - GET # キャッシュポリシーはAWSマネージドのキャッシュを全くしないポリシーを指定 CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad OriginRequestPolicyId: !Ref CloudFrontOriginRequestPolicy ResponseHeadersPolicyId: !Ref CloudFrontResponseHeadersPolicy Compress: false SmoothStreaming: false Origins: - Id: !Sub AppSyncOrigin-hironoenterprise-${SubName}-https # オリジンには AppSync の GraphQL エンドポイントを指定、FQDN 形式でないとエラーになる DomainName: !GetAtt AppSyncApi.GraphQLDns CustomOriginConfig: HTTPPort: 80 HTTPSPort: 443 OriginProtocolPolicy: https-only OriginSSLProtocols: - TLSv1.2 # ここで、オリジンに送るカスタムヘッダーを追加している OriginCustomHeaders: - HeaderName: x-origin-verify HeaderValue: !Ref CloudFrontOriginVerificationKey ConnectionAttempts: 3 ConnectionTimeout: 10 ViewerCertificate: AcmCertificateArn: !Sub "arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${CertificateId}" MinimumProtocolVersion: TLSv1.2_2021 SslSupportMethod: sni-only Tags: - Key: Cost Value: !Sub hironoenterprise-${SubName} DependsOn: - CloudFrontOriginRequestPolicy - CloudFrontResponseHeadersPolicy CloudFrontOriginRequestPolicy: Type: AWS::CloudFront::OriginRequestPolicy Properties: OriginRequestPolicyConfig: Name: !Sub OriginRequestPolicy-hironoenterprise-${SubName}-appsync Comment: !Sub CloudFront Origin Request Policy for hironoenterprise-${SubName}-appsync CookiesConfig: CookieBehavior: none # オリジンに転送するヘッダーは host 以外全てにした HeadersConfig: HeaderBehavior: allExcept Headers: - Host QueryStringsConfig: QueryStringBehavior: all CloudFrontResponseHeadersPolicy: Type: AWS::CloudFront::ResponseHeadersPolicy Properties: ResponseHeadersPolicyConfig: Name: !Sub ResponseHeadersPolicy-hironoenterprise-${SubName}-appsync Comment: !Sub CloudFront Response Headers Policy for hironoenterprise-${SubName}-appsync # CORS の設定は CorsConfig: AccessControlAllowCredentials: false # ここに Authorization を追加しないと動かなかった AccessControlAllowHeaders: Items: - Origin - Content-Type - Authorization - x-amz-user-agent # Query は POST メソッド。プリフライトチェックが行われるので OPTIONS を必ず入れる。 AccessControlAllowMethods: Items: - POST - OPTIONS # アクセスを許可するアプリの URL を入れる。ここでは hironoenterprise.com AccessControlAllowOrigins: Items: - !Sub https://${DomainName} # 必ず CORS ヘッダーをオーバーライドすること。 OriginOverride: true # セキュリティヘッダーはおまけ。一般的な設定。 SecurityHeadersConfig: ContentSecurityPolicy: ContentSecurityPolicy: !Sub "default-src 'self' *.${DomainName} ${DomainName}" Override: true ContentTypeOptions: Override: true FrameOptions: FrameOption: DENY Override: true ReferrerPolicy: Override: true ReferrerPolicy: strict-origin-when-cross-origin StrictTransportSecurity: AccessControlMaxAgeSec: 31536000 IncludeSubdomains: true Override: true Preload: true XSSProtection: ModeBlock: true Override: true Protection: true CustomHeadersConfig: Items: - Header: Cache-Control Value: no-store Override: true # ------------------------------------------------------------# # Route 53 ※独自ドメインを使用するときは CloudFront にエイリアスレコードを登録する # ------------------------------------------------------------# Route53RecordA: Type: AWS::Route53::RecordSet Properties: HostedZoneName: !Sub ${DomainName}. Name: !Sub hironoenterprise-${SubName}-appsync.${DomainName}. Type: A AliasTarget: HostedZoneId: Z2FDTNDATAQYW2 DNSName: !GetAtt CloudFrontDistributionAppSync.DomainName DependsOn: - CloudFrontDistributionAppSync Route53RecordAAAA: Type: AWS::Route53::RecordSet Properties: HostedZoneName: !Sub ${DomainName}. Name: !Sub hironoenterprise-${SubName}-appsync.${DomainName}. Type: AAAA AliasTarget: HostedZoneId: Z2FDTNDATAQYW2 DNSName: !GetAtt CloudFrontDistributionAppSync.DomainName DependsOn: - CloudFrontDistributionAppSync まとめ いかがでしたでしょうか。 いろいろ調べて非常に疲れました。HTTP ヘッダーの勉強になりました。同じ問題で困っている方、もし解決できたようでしたら、世の中に公開してくれると嬉しいです。または、AWS さんが AWS AppSync の CORS サポートを追加してくれればよいのですが。。。 本記事が皆様のお役に立てれば幸いです。
アバター
2025年2月以降のCatoクラウドのサービス体系(基本サービス、オプション、マネージドサービス)について解説をしています。 これまで(2025年1月末まで)のサービス体系や変更内容については、以下の記事をご参照ください。 Catoクラウドのサービス体系について(2024年版) Catoクラウド 2025年の価格改定(Pricing Update)について   基本サービス まず最初に、Catoクラウドの基本サービスとしては、以下の基本料金が発生します。 拠点毎のPoP接続帯域、および総帯域 モバイルユーザ Socket(エッジデバイス) 拠点毎のPoP接続帯域、および拠点総帯域 サービスメニューは、Catoクラウドを利用する国により異なります。 Catoクラウドでは、世界各国を大きく2つのグループ( Group1 、 Group2 )に分けられており、その他に、ひとつの単独国で価格設定している Stand-alone Countries に分けられています。Stand-alone Countries は、 中国 、 ベトナム 、 モロッコ の3ヵ国で、それぞれ国毎に価格設定がされているため、全部で以下の5つの料金体系となっています。 ちなみに、日本は、Group2 に所属します。 Group1(北アメリカ、ヨーロッパ) Group2(日本を含むアジア、南アメリカ、メキシコ、オーストラリア、アフリカ) Stand-alone Countries(中国) Stand-alone Countries(ベトナム) Stand-alone Countries(モロッコ) Group1、Group2、Stand-alone Countries を世界地図で表すと以下になります。 Site(サイト)ライセンス 次に、拠点のCatoクラウドへの接続については、” Site(サイト)ライセンス “というものが必要となり、本社、支店、営業所、店舗、データセンターといった物理拠点、または AWS、Azureなどのクラウド拠点に、このライセンスが必要となります。 Group1、Group2 の Siteライセンスは接続帯域毎に以下 10個のメニューが準備されています。 25Mbps(最小) 50Mbps 100Mbps 250Mbps 500Mbps 1,000Mbps 2,000Mbps 3,000Mbps 5,000Mbps 10,000Mbps(最大) 拠点は、契約帯域以上の通信速度は出ません。それ以上の通信はQoS設定に従い優先度の低い通信から破棄(Discard)されます。 Pooled(プールド)ライセンス また、Siteライセンス以外にも、複数接続拠点の総帯域を購入する” Pooled(プールド)ライセンス “があります。 Pooledライセンスは、Siteライセンスとは異なり、10Mbps単位で拠点分割が行え、拠点帯域の増速、減速をお客様が自由に行うことできるライセンスとなります。 Pooledライセンス契約は1,000Mbps以上(初期契約時は100Mbps単位で追加可)、契約後は200Mbps以上での追加となります。 Group1、Group2のグループ毎にPooledライセンスを契約し、グループ内での分配することが可能です。 ひとつのアカウントで、SiteライセンスとPooledライセンスの組み合わせも可能です。 Pooledライセンスは、Siteライセンスの100Mbpsベースの価格設定がされているため、 100Mbps以下の拠点の合計帯域が1,000Mbps以上となる場合は、Pooledライセンスを推奨 しております。特に、25Mbps以下の狭帯域(10M,20M)の拠点が多く存在する場合には、非常にコストメリットがあり、さらに増速・減速の柔軟性も確保することができます。 Stand-alone Countries のライセンス Stand-alone Countries については、Group1、Group2のメニューではなく、国内に閉じた通信(Regional)と国外向け通信(Global)を、それぞれ1Mbps単位で契約を行います。ともに2Mbps以上の契約が必要です。つまり、1拠点 Regional 2M/Global 2M が最小契約です。 Stand-alone Countries(中国、ベトナム、モロッコ)には、Pooledライセンスはございません。 価格としては、安価なのが Group1 で、次に Group2 、最も高額なのが Stand-alone Countries となります。 通常は、 SASEライセンス という後述のSocketの利用を前提としたライセンスになりますが、Socketを利用せずに、他の通信機器を利用したIPsec接続を行う場合には、より安価な SSEライセンス での契約を行うことも可能です。 SASEとSSEライセンスは、拠点毎で選択することが可能です。 モバイルユーザ(ZTNA) モバイルユーザ、ZTNA(Zero Trust Network Access)は、ユーザアカウント数による課金となります。 Group1、Gropup2については、共通の” Generalライセンス “となり、Stand-alone Countries(中国、ベトナム、モロッコ)は、それぞれ国毎のライセンスとなります。 購入したユーザ数以上は、登録が行えません。エラーになります。 モバイルユーザは、10ユーザから購入が可能となり、追加も10ユーザ単位の購入となります。また、Generalライセンス 5ユーザ分が予備で付与されています。 Generalライセンスについては、10~500、501~1,000、1,001~5,000、5,001~10,000、10,001・・・、と契約ユーザ数毎にボリュームディスカウント料金が適応されますが、Stand-alone Countries は、数量によるボリュームディスカウントはありません。 モバイルユーザは、端末にCatoクライアントをインストールしますが、 1ユーザ(アカウント)で3台(デバイス)まで利用することが可能 です。 Socket Catoクラウドの最大の特徴であるエッジデバイス、Socket(ソケット)についてです。Catoクラウドで拠点接続する際の通信機器(ルータ、ファイヤーウォール等)の代わりになるものです。 物理ハードウェアアプライアンスとして、 X1500 、 X1600 、 X1700 の3機種があります。X1500/X1700については、後継版の”B”となっています。 X1500の最大スループットが500Mbps、X1600が1,000Mbps、X1700が10,000Mbpsまでとなっております。 X1600については、通常(ベーシック)モデル以外に、SIMが搭載可能な「LTEモデル」がリリースされており、2025年に「Wi-Fiモデル」「Wi-Fi+LTEモデル」「5Gモデル」などがリリース予定となっています。 物理Socketは利用せず、仮想アプライアンス(vSocket)や、既存の追伸機器(ルータやFirewall)等を用いたIPsec接続のみを利用される場合は、Socketの費用は発生しません。 Socketは、冗長(HA)構成を行うことが可能です。また、コールドスタンバイ(予備機)として手配することも可能ですが、Socketは、購入するのではなく、サブスクリプション(サービス利用課金)となりますので、手配したSocketすべてに費用が発生します。 また、Socketのラックマウントキット、ウォールマウントキットも有り、同じくサブスクリプションで提供されています。 Catoクラウドのサービス終了時には、すべて返却(返送)いただく必要があります。 オプションサービス 2025年1月、現在以下の10個のオプションサービスがあります。 Catoクラウドの標準機能に、 SD-WAN 、 URLフィルタリングを含む SWG(セキュアウェブゲートウェイ) 、 Firewall は含まれています。 No. オプション オプションサービス内容 1 Threat Prevention アンチマルウェア(AM)、次世代型アンチマルウェア(NGAM)、不正侵入防止(IPS)、DNS セキュリティ、Threat Intelligence、インラインAI/ML、アンチフィッシング 2 Advanced Threat Prevention 上記のThreat Preventionに加えて、サンドボックス(Sandbox)、RBI(Remote Browser Isolation)を追加 3 CASB Cloud Access Security Broker シャドーIT、SaaS・アプリケーション利用の可視化/評価/制御 4 DLP Data Loss Prevention 機密情報や重要データの漏洩対策 5 SaaS Security API 外部クラウドサービスのAPIによるセキュリティ検査(アンチマルウェア、DLP) 6 XDR Security Pro Extended Detection and Response 拡張検出と対応 7 Digital Experience Monitoring Digital Experience Monitoring(DEM、デム) エンドユーザー体験監視 8 IoT/OT Security SASEベースの保護を、IoT/OT環境まで拡張し、デバイスの可視化とセキュリティ強化を実現 9 Endpoint Security(EPP) Endpoint Protection Platform エンドポイントプロテクションプラットフォーム 10 Data Log Storage Catoクラウドのデータ保管期間延長オプション Threat Prevention パターンファイルマッチングのアンチマルウェア(Anti-Malware)と、機械学習エンジンを用いた振る舞い検知を含む次世代型アンチマルウェア(Next Generation Anti-Malware)、不正侵入検知システム(IPS)、不正なドメインへのアクセスをブロックする DNS Protection 、不審なアクセスをモニタリングする Suspicious Activity Monitoring(SAM)、Threat Intelligence、インライン AI/ML、アンチフィッシングなどの基本的な脅威対策がすべて含まれているCatoクラウドで最も契約されている基本的なセキュリティオプションとなります。 IPSは、XDR Security Coreにも利用されています。 Advanced Threat Prevention Threat Preventionに加えて、Remote Browser Isolation(RBI)とサンドボックス(Sandbox)が追加されたセキュリティオプションとなります。 RBIは、エンドユーザーのデバイス(PC等)の代わりに、Catoクラウドが、ユーザーのWeb閲覧セッションを代わりに実行し、その画面情報をユーザへ送信することによって、オンラインサイトからの脅威(不正プログラムのダウンロードや実行)を無力化するものです。 RBIは、別のセキュリティオプションとして販売されていましたが、2025年2月以降、単体セキュリティオプションとしての購入できなくなりました。 サンドボックスは、Cato Networks社のこれまでの方針では、昨今の俊敏な企業ニーズには合致しないとし、ゼロデイ脅威対策や、脅威を含む未知ファイルを防ぐための異なるアプローチを取っていましたが、多くのお客様・市場のニーズに応じて、機能提供を開始しました。 サンドボックスは、ファイル(プログラム)を保護・隔離された環境で、システムやデータに影響を与えずに実行し、マルウェア検出やプログラムの動作検証を行うものですが、Catoのサンドボックスは、俊敏性を阻害する事前の予防処理を行うのではなく、処理を並行で実施し、事後にプログラムの動作検証レポートを行うものとなります。 CASB SaaSやクラウドサービスの利用状況を可視化(=シャドーITの可視化)を行います。Cato社で各アプリケーションを独自のセキュリティ・コンプライアンス等の視点で評価した Application Credibility Evaluator(ACE)を利用しており、それを元に管理者が、アプリケーション毎に利用許可(Sanction)を行うことが可能になります。さらにアプリケーションのアクティビティ単位での制御を行うことが可能になります。例えば、DropboxやGmailでダウンロード(download)は許可するが、アップロード(upload)は許可しないなどです。また、Office365の企業テナントのみの利用を許可する(個人利用は許可しない)なども、CASBオプションで実現が可能となります。 DLP トラフィック上のすべてのファイルをスキャンして、機密情報の検出を行い、適切な措置を講じることができます。機密情報の特定には、事前にCato社で定義されたルール(データタイプ)を利用することも可能です。クレジットカードやマイナンバーカードなどは事前にルールが定義されていますが、個別に定義することも可能で、MIP(Microsoft Information Protection)ラベルとの連携も可能になっています。もちろん日本語にも対応しています。 SaaS Security API SaaSアプリケーションに対して、APIを利用してセキュリティ検査(マルウェア検査やDLP)を行う機能です。 Catoクラウド以外から、SaaSやクラウドサービスを利用する場合、つまり社外とのコラボレーションを行う際の脅威を検出するためのセキュリティオプションとなります。 Microsoft Exchange/OneDrive/SharePoint、Google Drive/Gmail、Box、Slack、Salesforce、ServiceNow、GitHub、Workspace(Meta)に対応しております。 XDR Security Pro CatoのXDR Securityは、世界初のSASEベースのXDR(Extended Detection and Response)です。 XDR Securityには、CoreとProの2種類があり、 XDR Security Core は、Catoクラウドをご利用のすべてのお客様が無料でご利用いただけますが、Coreは、IPSを元にセキュリティインシデントの分析をしていますので、Threat Preventionのご利用が前提となります。 また、Microsoft Defender for Endpointと連携が可能で、主要EDR製品(CrowdStrike、SentinelOne)との連携も計画されています。 XDR Security Pro は、セキュリティインシデントに対する対応(SOC通知)が可能なお客様向けに提供される機能で、AIベースの脅威ハンティング(Threat Hunting)、ユーザー行動分析(User Behavioral Analysis)、インシデントライフサイクル管理を追加した有償のオプションとなります。Core同様Threat Preventionの契約が前提となります。 また、後述するCato社のマネージドサービスであるMDRは、このXDR Security Proの契約が前提になります。 Digital Experience Monitoring Digital Experience Monitoring(DEM、デム)は、アプリケーションのユーザー体験を監視するテクノロジーで、エンドユーザーとアプリケーション間のすべてのシステムの健全性を計測し監視します。 DEMは、エンドユーザーのデジタル体験を包括的に監視・分析し、SaaS、クラウド、プライベートアプリケーションのパフォーマンスをリアルタイムに把握することができ、通信経路上のボトルネックや問題の早期発見と迅速な解決が可能となる有償のオプションです。 高度な AI 分析を活用し、インターネット、WAN、カスタムアプリケーション全体のユーザー エクスペリエンスを監視し、最適なパフォーマンスを確保することができます。 アラート機能と高度なトラブルシューティング機能を活用し、エンドポイントの問題を迅速に特定し、課題解決時間を大幅に短縮することができます。 Device Monitoring機能が、エンドユーザのデバイスのパフォーマンスや、利用しているWi-Fiのパフォーマンスを監視し、Synthetic Probe Minitoring機能が、LAN・Socket・インターネットG/W・トンネル・アプリケーションのパフォーマンスを監視し可視化することで、通信経路上のボトルネックをより詳細に特定することが可能になります。 IoT/OT Security IoT/OT Security は、SASEベースの保護を、IoT/OT環境まで拡張し、リアルタイムにデバイスの検出と分類を行い、きめ細かなポリシー適用を行うことで、包括的な脅威防御を提供します。 本オプションは、Site(およびPooled)のみに適用されるオプションです 。 Catoクラウドではこれまで、Site(Pooled)とモバイルユーザは必ずセットでオプション契約が必要でしたが、このオプションについては、Site(およびPooled)にのみ適用されるオプションとなります。 Endpoint Security(EPP) 世界初のSASEベースのエンドポイントセキュリティ(EPP)ソリューションとなります。これまでのSASEのカバレッジ範囲を、ネットワーク層を超えてエンドポイントにまで拡張した製品となり、Cato管理アプリケーション(CMA)に完全に統合管理され、クラウドネイティブな他のセキュリティスタックと連携して動作します。 EPPは、端末にEPPソフトウェアをインストールします。モバイルユーザの利用デバイス数上限と同じく上限は3デバイスとなります。 Data Log Storage(保管期間延長) Catoクラウド上での標準のデータ保管期間は3ヶ月(1時間あたりのイベント数が250万以下)です。 Data Log Storage とは、1時間あたりのイベント出力数(標準は250万イベント/時以下)の上限緩和や、データ保管期間を3ヶ月から6ヶ月または12ヶ月へ延長するオプションとなります。 保管期間の延長を行うと、イベントログだけはなく、トラフィックデータなども保管期間が延長され、過去に遡って確認することが可能となります。 No. ログ数(時間単位) 3ヶ月 6ヶ月 12ヶ月 1 250万イベント以下 0円(標準) ¥ ¥¥ 2 250万イベント ~ 500万イベント以下 ¥ ¥¥ ¥¥¥ 3 500万イベント ~ 750万イベント以下 ¥¥ ¥¥¥ ¥¥¥¥ グローバルIPアドレス(4つ目以上) お客様毎に専用で個別割り当てが可能なグローバルIPアドレスは、Catoクラウドでは標準で3つまではサービスに含まれていますが、4つ目以上は、オプション(追加課金)となります。 オプションの課金体系について Site(Pooled)ライセンスとモバイルユーザは、必ず同じオプションを選択する必要があります(ただし、IoT/OT Serurity除く) つまり、1拠点だけ、モバイルユーザだけオプションを選択しないと言った契約はできません。 Threat Prevention、Advanced Threat Prevention、CASB、DLP、IoT/OT Securityは、PoP接続帯域と同じく 帯域課金 となっております。 IoT/OT Serurityのみが、Site(Pooled)ライセンスにのみ適用されますが、その他のオプションは、モバイルユーザにも課金されます。 SaaS Security API、XDR Security Pro、DEM、EPP、MDRは、 Knowledge Users(ナレッジユーザ)課金 となります。ナレッジユーザとは、企業におけるM365やG-Suiteの契約ユーザ数のことで、同契約ユーザ数での契約が必要となります。 オプションの利用条件について オプションには幾つかの前提条件があります。 DLPは、CASB契約が前提となります。 SaaS Security APIは、DLP契約が前提となります。 SaaS Security APIでマルウェア検査をする場合は、Threat Preventionの契約が前提となります。 Cato社のマネージドサービスであるMDRを契約する場合は、XDR Security Proの契約が前提になります。   マネージドサービス 次に、Cato Networks社自身が提供するマネージドサービスについて紹介します。 No. マネージドサービス サービス内容 1 MDR Managed Detection & Response 専任のセキュリティアナリストによるSOCサービス  ※日本語未対応 2 ILMM Intelligent Last Mile Management ラストマイルインターネット回線管理サービス 3 NOCaaS NOC-as-a-Service パートナー向け NOC サービス   MDR Cato社にて専任のセキュリティ専門家が、アセスメントから、ゼロデプロイメント、全てのトラフィック常時監視し、継続的な脅威ハンティングをサービス提供します。定期的なレポートとサービスレビュー(オンライン会議)が行われます。 残念ながら、 2025年1月時点においても、MDRは英語対応のみ(レポートおよびオンラインのレビュー会議等)となっており、 日本語は未対応 となっております。 そのため、SCSKでは個別に日本語対応したSOCサービスをご提供しております。 ILMM Cato社のNOC(Network Operations Center)が、ラストマイルのインターネット回線のブラックアウト(回線断)、ブラウンアウト(回線の品質やレスポンスが規定に満たない状況)、パフォーマンス低下やをリアルタイムに監視(検知)します。 NOCが問題を検知し、回線が原因であることを特定すると、Cato社が直接ISPへ(日本国内の場合は日本語で)連絡を行い、問題の解決を図ります。ISPと協力し、ネットワークの問題原因を特定し、問題解決を図り、お客様へ対応内容を適宜ご報告します。 ISPには、事前にお客様の委任状をいただくことで、ISPへの直接問い合わせを代理で実施します。 特に、海外の拠点へ多く展開されており、ご担当者は日本にしか居ない場合は、時差等の問題もあり、ラストマイルのインターネット回線不具合をCato社のNOCが管理を代行することが大きなメリットとなります。 NOC-as-a-Service Cato社のNetwork Operations Centre as a Service (NOCaaS) は、お客様に NOC サービスを提供したい パートナー向けサービス となります(2024年10月販売開始) NOCaaS は、ネットワーク監視およびトラブルシューティングサービスを提供する専門のエンジニアチームを提供し、すべてのお客様のネットワークが最大限の稼働時間とパフォーマンスで最適な状態で稼働するように支援します。NOCaaS には ILMMやハンズフリー管理(Hands-free Management)が含まれています。 ハンズフリー管理(Hands-free Management)は、Cato社またはパートナーのいずれかが、お客様のネットワークの完全なハンズフリー管理を行うサービスです。専門スタッフが、ビジネスや技術要件の変更に伴うネットワークやセキュリティポリシーの変更をすべて行います。お客様、パートナー(またはCato)による、共同管理モデルでもご利用いただけます。 SCSKでは、NOC-as-a-Serviceやハンズフリー管理については、SCSKでは、サービス窓口、監視・障害一次切り分け、変更作業代行、月次報告などの各種マネージドサービスを取り揃えております。 マネージドサービスの課金体系について MDRはナレッジユーザ課金となります。 ILMM、NOCaaSは、お客様の全てのサイトに対し適用されることが前提で、特定サイトのみでのご利用はできません。 料金は、1 サイト単位、またはPooledライセンスの場合は 1 Mbps 単位での提供となりますが、別途最低契約金額の設定が存在します。   契約、課金、最小構成について 契約期間について Catoクラウドの ご契約期間は 1年間(最低) となります。 移行期間や、これまでのWANのリプレースであることから2年以上の複数年契約を行われるお客様も多くいらっしゃいます。 複数年契約を行う場合の注意点としては、Catoクラウドでのライセンス追加(拠点追加、帯域増速、モバイルユーザ追加、あるいはSocketのアップグレード)は、契約期間中であれば、いつでも実施することが可能ですが、ライセンス削減(拠点削減、帯域減速、モバイルユーザ削減、Socketダウングレード)は、契約更新時にしか実施することができませんのでご注意ください。 また、契約期間中のライセンス追加については、契約満了月までの契約となります。 例えば、2025年2月1日契約開始で、2026年1月末契約終了の1年契約の場合、2025年4月に拠点を増速した場合は、ライセンス追加分の契約は、2025年4月から2026年1月の10ヵ月の契約となります。 課金(請求)について SCSKでは、 課金(請求)については、月額課金(請求)、一括請求ともに可能 です。 基本サービスとオプションサービス、マネージドサービスをお客様の要望に応じてご請求が可能ですが、月額課金(請求)のお客様が殆どとなります。 Siteライセンスの増速(例 25Mbps→50Mbps)や、モバイルユーザの追加(例 +10ユーザ)も、追加した月からの追加課金(または残期間の一括課金)となります。 Socketについてもサブスクリプションですので、アップグレードが可能です。X1500からX1600、X1700へのアップグレードを実施した場合は、アップグレード実施月からの追加課金(請求増)となります。 最小構成 Catoクラウドの 最小構成は、1 Siteライセンス、10 モバイル(ZTNA)ユーザ となります。 上記の最小構成は、モバイルユーザ 10名と、拠点しては、クラウド(AWS)としてのSiteライセンス 25Mbps(最小)としています。 モバイルユーザ(日本)については帯域の制限(上限)はありませんが、一部の地域(中国やベトナム等)については上限があります。 AWSのvSocketには料金は発生しません。ただし、AWSの利用量(仮想マシン利用、通信量等)は別途必要です。 最小構成の費用感としては、定価ベースで年間65万以下(月額6万円以下)となりますので、他のSASEソリューション、日本国内のソリューションと比較しても、非常に安価でスモールスタート可能なソリューションです。 見積り・ライセンス発行における注意事項 見積りおよびライセンス発行等の各種手続き、課金(請求)については、Catoクラウドのパートナーにより異なります ので、各種手続きや特にリードタイムを事前にご確認されることを推奨します。 例えば、拠点帯域が逼迫した際、見積りやライセンス発行手続きが遅いと、業務に支障がでる期間が長くなる可能性があります。特にCatoクラウドは、クラウドサービスですので、スモールスタートのお客様が殆どですので、ご契約されるパートナーのリードタイムには十分ご注意ください。 SCSKでは、 ライセンス追加(拠点追加・帯域増速・モバイルユーザ追加)は、15時までの手配(注文書の受領)で、最短で当日中のライセンス発行が可能 です。   SCSKのマネージドサービスについて SCSKでは、2019年よりCatoクラウドの取り扱いを開始し、すでに50社以上のお客様とご契約を行っており、お客様ニーズに応じてこれまで様々なマネージドサービスのご提供を行っておりますので、詳細については以下を参照ください。 Catoクラウドのサービス体系について(2024年最新版) 2024年2月以降のCatoクラウドの新しいサービス体系(基本料金、オプション料金、マネージドサービス)について解説を行っています。 blog.usize-tech.com 2024.01.29 また、これまでの個別サービスメニュー提供に加え、2025年2月からは、新たなサービスメニューも加えた上で、” マネージドサービスパック “をリリースする予定です。   まとめ Cato Networks社 の Catoクラウドのサービス体系、基本サービス、オプションサービス、Cato社のマネージドサービスから、契約・課金(請求)、最小構成、SCSKマネージドサービスについても合わせてご紹介させていただきました。 少しでもCatoクラウドに興味をお持ちになられた方は、遠慮なくSCSKまで お問い合わせ ください。 日本国内でも”SASE(サッシー)”や、Cato Networks、Catoクラウド(Cato Cloud/Cato SASE Cloud)の認知度が徐々に上がって来ておりますが、まだまだ低い状況です。 SCSKでは、2021年からSASEの主要な4ソリューションを一同に紹介を行うオンラインセミナー「 SCSK SASE Solution Summit(通称 S4 エスフォー) 」を定期的に開催しております。これまで15回開催し、述べ2,200名以上の方にご参加いただいております。 また、Catoクラウドは、 デモセミナー(オンライン) 、 ハンズオンセミナー(オフライン) なども定期的に開催しておりますので、ご興味ある方は、ぜひご参加ください。 Catoクラウド イベント・セミナー情報 | よくあるご質問 | Cato Cloud ケイトクラウド - SCSK 定期的にCatoクラウドをご紹介するセミナーを開催しております。・2024年9月26日(木)Catoクラウド ハンズオンセミナー【札幌開催分】 ~全国5都市開催(東京/大阪/名古屋/博多/札幌)~ ... cato-scsk.dga.jp SASEセミナー、Catoクラウドセミナー以外に、Catoクラウドの お客様導入事例 の制作、 FAQサイト運営 、この  TechHarmony(技術ブログ) で、Catoクラウドの日本語のお役立ちサイトを目指し、少しでもCatoクラウド、SASEの認知度向上と、皆様のお役に立てればと考えておりますので、今後ともよろしくお願いします。 Catoクラウド エンジニアブログまとめ記事 Catoクラウドのエンジニアブログ(TechHarmony)でのまとめ記事となります。 blog.usize-tech.com 2024.02.27
アバター
SCSKの畑です。5回目の投稿です。 第一回 の続きです。今回は、前回説明した排他制御の仕組みをアプリケーションからどのように使用しているかが主な内容となります・・が、それだけだと書くことがほぼなくなってしまうので、使用するための前提条件などを補足しながらにしようと思います。 アーキテクチャ概要、及び排他制御に使用したサービス / コンポーネント 今回はほぼ AppSync & アプリケーションの話ですが、排他制御の仕組みを使用するにあたりメンテナンス対象である Redshift についても補足が必要なため、合わせて言及します。 Amazon DynamoDB テーブルのステータス(編集状態)管理 AWS Lambda 排他制御を考慮したテーブルのステータス更新ロジックの実装 AWS AppSync(AWS Amplify) 上記 Lambda をアプリケーション上から実行するためのスキーマ定義 アプリケーション(Nuxt.js) テーブルのステータスに応じた画面制御 アプリケーションにおける設計・実装 まずは、対象テーブルを通常状態から編集状態に移行する関数「setStatusEditingContent」の実装例を以下に示します。この関数を画面上のフォーム/ボタンに紐付けて実行するイメージです。 import * as mutations from "@/src/graphql/mutations"; import * as models from "@/src/API"; const setStatusEditingContent = async () => {   // 編集対象テーブルの引数定義   const input_variables:models.ChangeTableStatusWithLockInput = {       table_name: table_name,       current_status: models.TableStatus.normal,       target_status: models.TableStatus.editing,       editor: userinfo['user_name'],   }     // 編集対象テーブルのFK参照先テーブルの引数定義   const fk_tables = getTableFKList()   if (fk_tables.length > 0) {       input_variables['fk_table_name'] = fk_tables       input_variables['fk_current_status'] = models.TableStatus.normal       input_variables['fk_target_status'] = models.TableStatus.locked       input_variables['locked_by'] = table_name   }     // 排他制御を伴うステータス変更(通常状態 -> 編集状態)   const result = await client.graphql({       query: mutations.ChangeTableStatusWithLock,       variables: {           input: input_variables       }   })     // 編集状態にステータスを変更出来なかった場合   if (result.data.ChangeTableStatusWithLock.lock_result == false) {       // 対象テーブルのステータスが変更出来なかった場合       if (result.data.ChangeTableStatusWithLock.current_status === models.TableStatus.editing) {           alert('編集モードに移行できませんでした。既に他のユーザが編集中です。')       } else if (result.data.ChangeTableStatusWithLock.current_status === models.TableStatus.locked) {         alert('編集モードに移行できませんでした。他のユーザが編集中のテーブルのFK参照先テーブルのため、ロックされています。') }       // 対象テーブルのFK参照先テーブルのステータスが変更出来なかった場合       else {           const lock_failed_tables = []           for(const fk_table_status of result.data.ChangeTableStatusWithLock.fk_current_status){               if (fk_table_status.current_status != models.TableStatus.normal){                   lock_failed_tables.push(fk_table_status.table_name)               }           }           alert(`編集モードに移行できませんでした。以下のFK参照先テーブルのロックが取得できませんでした。ロック解放後に再度試行ください。\n[${lock_failed_tables.join(', ')}]`)       }   } } 内容自体は特に捻りもなく、前回定義した mutation の引数を最初に定義した上で graphql client から mutation を実行し、ステータス変更に失敗した場合は画面上にそのエラーを出力しています。(ステータス変更に成功した場合は画面遷移するため特に出力なし) 状態遷移に関しても、前回定義した状態遷移図に基づき、対象テーブルと FK 参照先テーブルで引数にてそれぞれ (fk_)current_status と (fk_)target_status を指定しています。ただ、FK 参照先テーブルの情報について定義している部分について、そもそもどのように FK 関連の情報を取得してきているのかここまで触れてこなかったので、この点について次のセクションで補足します。 対象 Redshift テーブルの FK 情報取得について 本アプリケーションでは、アーキテクチャ図の通り Redshift のテーブル情報を S3 上で管理しています。具体的には、アプリケーションより対象テーブルのデータ参照/編集画面にアクセスした時点で、Redshift に対して SQL 文を実行してテーブル定義情報及びデータ情報を S3 上に保存し、そのデータをアプリケーションから取得してテーブルデータを表示しています。Redshift については AppSync がネイティブ対応していないこともあり、前回説明した mutation と同じく実処理は Lambda で実装しています。 Lambda 自体は様々な要件なり理由から実装が一番複雑になってしまったこともあり今回説明はしませんが、実行している SQL 文について以下に示します。Redshift は PostgreSQL をベースとしているため、 PostgreSQL のシステムカタログを複数結合して対象テーブルの制約情報を取得しています。 なお、Redshift は DWH サービスである以上、RDBMS である PostgreSQL の機能が全てサポートされている訳ではありません。このため、一部の機能差異については今回アプリケーション側の実装でカバーする必要がありました。そのあたりの話題については、また別のエントリでまとめたいと思います。 Amazon Redshift および PostgreSQL - Amazon Redshift Amazon Redshift と PostgreSQL の設計上の違いについて説明します。 docs.aws.amazon.com SELECT     c.conname AS constraint_name,     CASE         WHEN c.contype = 'p' THEN 'PK'         WHEN c.contype = 'u' THEN 'UK'         WHEN c.contype = 'f' THEN 'FK'     END AS constraint_type,     t.relname AS table_name,     a.attname AS column_name,     CASE         WHEN c.contype = 'f' THEN rt.relname         ELSE NULL     END AS referenced_table_name,     CASE         WHEN c.contype = 'f' THEN ra.attname         ELSE NULL     END AS referenced_column_name FROM     pg_constraint c JOIN     pg_class t ON c.conrelid = t.oid JOIN     pg_attribute a ON a.attrelid = t.oid  AND a.attnum = ANY(c.conkey) JOIN     pg_namespace nsp ON c.connamespace = nsp.oid LEFT JOIN     pg_class rt ON c.confrelid = rt.oid LEFT JOIN     pg_attribute ra ON ra.attrelid = rt.oid  AND ra.attnum = ANY(c.confkey) WHERE    t.relname = '<table_name>' AND nsp.nspname = '<schema_name>' ORDER BY     c.conname, column_name; 上記 SQL 文をクエリエディタ経由で実行した例です。対象テーブル「m_country」の制約情報が表示されています。 この内、constraint_type 列が「FK」の行が FK 関連の情報を示しています。 同行における referenced_table_name が FK 参照先テーブル名を示しており、この情報を先述したステータス更新用の mutation を実行する際に使用しています。 そして、アプリケーション上から FK 関連情報を取得するための関数が「getTableFKList()」となります。 まとめ やはり、第一回の最後で危惧していた通り、分量が若干アンバランスになってしまった感があります。。このため第三回として、排他制御関連のトピックとして少し毛色の違う内容を取り上げてみようと思います。 本記事の内容がどなたかの役に立てば幸いです。
アバター
本記事は、以下Cato Networks社の記事を日本語へ意訳し、再構成したものとなります。 Cato IoT/OT Security   Cato IoT/OTセキュリティについて Cato IoT/OT Security は、2024年12月にリリースされた新機能です。 IoT(Internet of Things) 、 OT(Operational Technology) 環境まで保護を拡張し、リアルタイムにデバイスの検出と分類を行い、きめ細かなポリシー適用を行うことで、包括的な脅威防御を提供します。 Catoクラウド プラットフォームのネイティブ機能として、Cato IoT/OT Securityは、複雑なインテグレーションを一切排除されており、ご契約ですぐに利用が可能で、管理も簡素化することが可能です。 デバイスの即時発見と分類 Cato クラウドプラットフォームにネイティブに組み込まれたCato IoT/OTセキュリティは、構築・インテグレーションの必要が一切なく、IT、IoT、OT環境全体にわたって即座に可視性をお客様へ提供します。専用に構築され、訓練されたAIとML(機械学習)がネットワーク上のデバイスをフィンガープリントし、タイプ、メーカー、バージョンをマッピングします。IoT/OTの盲点をなくすことで、この重要な攻撃対象領域を完全に把握することができ、堅牢なIoT/OTセキュリティ戦略の基盤となります。 きめ細かなポリシー適用 完全なIoT/OTセキュリティ戦略には、可視化以上のものが必要であり、IT部門は、社内外のリソースへのIoT/OTデバイスのアクセスを効率的に制御する能力が必要です。 Cato IoT/OT Securityは、IT部門がこれらを実現できるよう支援します。アクセスポリシーは、特定のデバイスの特性に基づいて設定することも、効率化のためにタイプ、メーカー、モデルによるグループ化に基づいて設定することもできます。IoT/OTネットワークアクセスを制御することで、企業ネットワークの安全性を確保し、攻撃対象領域を劇的に減らすことができます。 総合的な脅威防御の実現 Cato IoT/OT Securityは、ポリシーの実施だけでなく、Cato クラウドプラットフォームの脅威防御機能からもメリットを得られます。 Catoの脅威防御は、IPS、NGAM、DNSセキュリティ、インラインAI/MLなどの複数の高度なセキュリティエンジンを活用し、既知の脅威やゼロデイ脅威からIoT/OTデバイスを保護します。企業は、高度な防御によってリスクを軽減することができます。 管理の簡素化 Cato IoT/OTセキュリティは、Cato Management Application(CMA)のネイティブで統合されています。 すべてのダッシュボードとレポートは、CMAとCatoクラウドプラットフォームの他の機能と一貫しているため、新しいインターフェイスを学習する必要がなく、Time-to-Value(価値を実感するまでにかかる時間)を加速します。 ポリシー、ログ、イベント、およびデバイス、メーカー、モデル、ユーザー、サイトなどのオブジェクトは、CMA全体で一貫して表示されます。この一貫性により、ITチームは、複数のソリューションにまたがる異なるダッシュボード間を移動する必要なく、IoT/OTインシデントを容易に特定して理解することができます。 セキュリティ製品の乱立解消 IoTとOTのセキュリティは、従来、複数のハードウェアとソフトウェアのソリューションの展開と統合を意味していました。このアプローチは、しばしば複雑さをもたらし、セキュリティギャップを作り出し、ネットワークの待ち時間を増やします。 Cato IoT/OT Securityは、Cato SASE Cloud Platformのネイティブな一部であり、セキュリティとネットワーキングを統合し最適化する単一のクラウド提供ソリューションによって、すべてのIoT/OTセキュリティ要件に対応します。この統一されたアプローチにより、従来のソリューションの複雑さが解消され、運用のオーバーヘッドなしに堅牢なセキュリティが提供されます。 まとめ 少しでもCatoクラウドに興味をお持ちになられた方は、遠慮なくSCSKまで お問い合わせ ください。 SASE、Cato Networks、Catoクラウド(Cato Cloud/Cato SASE Cloud)の知名度がまだまだ低い状況です。 SCSKでは、2021年から主要なSASEソリューションを一同に紹介を行うオンラインセミナー「 SCSK SASE Solution Summit(通称S4 エスフォー) 」を定期的に開催しており、これまで15回開催し、述べ2,200名以上の方にご参加いただいております また、Catoクラウドについては、 デモセミナー(オンライン) 、 ハンズオンセミナー(対面) なども無料で開催しておりますので、ご興味ある方は、ぜひご参加ください。 Catoクラウド イベント・セミナー情報 | よくあるご質問 | Cato Cloud ケイトクラウド - SCSK 定期的にCatoクラウドをご紹介するセミナーを開催しております。・2024年9月26日(木)Catoクラウド ハンズオンセミナー【札幌開催分】 ~全国5都市開催(東京/大阪/名古屋/博多/札幌)~ ... cato-scsk.dga.jp SASEセミナー、Catoクラウドセミナー以外に、Catoクラウドのお客様導入事例の制作、 FAQサイト運営 、この  TechHarmony(技術ブログ) で、Catoクラウドの日本語のお役立ちサイトを目指し、皆様のお役に立て、少しでもCatoクラウド、SASEの知名度アップに貢献できればと考えておりますので、今後ともよろしくお願いします。 Catoクラウド エンジニアブログまとめ記事 Catoクラウドのエンジニアブログ(TechHarmony)でのまとめ記事となります。 blog.usize-tech.com 2024.02.27
アバター
本記事は、以下Cato Networks社の記事を日本語へ意訳し、再構成したものとなります。 Cato Digital Experience Monitoring(DEM)   Cato DEMについて Cato DEMは、2024年11月3日にリリースされた新機能で、エンドユーザーエクスペリエンスへのリスクを最小限に抑えながら、IT部門が戦略的なデジタルトランスフォーメーションプロジェクトをサポートできるようにするものです。 エンドユーザーの利用状況を監視、分析を行い、パフォーマンス問題をプロアクティブに特定して解決することで、最適なユーザーエクスペリエンスを確保し、障害やトラブルを事前に減らすことで、IT運用を合理化を促進します。 デプロイ・構築作業が不要で、即座に利用可能 Cato DEMは、Cato Cloud Platformの一つの機能です。 IT部門は、簡単にDEMを有効化し、すぐにその利点を体験することができます。ソリューションアーキテクチャとしては、新たなセンサー配置や、インテグレータ作業は一切不要です。DEMのデータは、Catoクライアント、Cato SD-WAN、Cato PoP、Catoグローバルバックボーンなどの既存Catoクラウドの各センサーから自動的に収集されます。パフォーマンスとエクスペリエンスのベースラインは自動的に計算され、DEM ダッシュボードとレポートで IT部門へ提示されます。 何がユーザーエクスペリエンスを阻害しているかを確認、即座に修正 Cato DEMでユーザーエクスペリエンス問題を迅速に特定することが可能になります。 Cato DEMは、企業ネットワーク全体のユーザーとアプリケーションのパフォーマンスを可視化することで、問題を迅速に特定し、迅速に解決することができます。Cato DEMは、ユーザーやデバイスからWAN、インターネット、SaaS上のアプリケーションまで、詳細な分析とホップ・バイ・ホップの可視性を提供することができます。エクスペリエンスの問題の根本原因が、デバイス、ネットワーク、アプリケーションのいずれにあるかに関わらず、それを迅速に検出・特定できます。IT 部門は迅速かつ効率的に問題に対処できるようになり、ダウンタイムや煩雑な作業、フラストレーションを回避できます。 ユーザーエクスペリエンス問題をプロアクティブに特定し対処可能 ユーザーエクスペリエンスの問題への対応から、積極的なユーザーエクスペリエンスの確保へ。 Cato DEMのAI搭載エンジンを搭載しており、実際のユーザーモニタリングと合成プロービングを相関させ、ユーザー、デバイス、場所に関係なく、ネットワークパスのパフォーマンスを継続的にチェックします。潜在的な問題を事前に特定して対処することで、IT部門は、クラウド移行やグローバル展開などの戦略的プロジェクトにおいて、最適なユーザーエクスペリエンスを確保できます。プロアクティブなアプローチにより、全体的な生産性と満足度が向上します。 インシデント管理の標準化によるIT効率の向上 DEMストーリー(≒インシデント)は、相関する一連のパフォーマンス問題を特定し、CatoのSASEベースのXDRに優先順位付けされたリストとして表示されます。各 DEMストーリーには、調査のための完全な詳細とコンテキストが含まれ、AI と ML(機械学習)の洞察によってデータ駆動型の意思決定が強化され、調査時間の短縮に役立ちます。Cato XDRでセキュリティとネットワークに関する洞察を統合することで、チーム間のコラボレーションが向上し、業務効率が大幅に改善されます。 DEMとSASEのすべてを一枚のガラスで実現 Cato DEMは、Cato Management Application (CMA)のネイティブコンポーネントです。 すべてのダッシュボードやレポートは、CMAやCato Cloudのプラットフォームの他の機能と一貫しているため、長い学習曲線や価値の実現に時間がかかることがありません。ポリシー、ログ、イベント、およびユーザー、サイト、アプリケーションなどのオブジェクトは、プラットフォーム全体で一貫性があり、統一されています。この一貫性により、ITチームは、複数のソリューション間で異なる参照やプレゼンテーションの間でマッピングする必要なく、データポイントを容易に理解し、関連付けることができます。 まとめ 少しでもCatoクラウドに興味をお持ちになられた方は、遠慮なくSCSKまで お問い合わせ ください。 SASE、Cato Networks、Catoクラウド(Cato Cloud/Cato SASE Cloud)の知名度がまだまだ低い状況です。 SCSKでは、2021年から主要なSASEソリューションを一同に紹介を行うオンラインセミナー「 SCSK SASE Solution Summit(通称S4 エスフォー) 」を定期的に開催しており、これまで15回開催し、述べ2,200名以上の方にご参加いただいております また、Catoクラウドについては、 デモセミナー(オンライン) 、 ハンズオンセミナー(対面) なども無料で開催しておりますので、ご興味ある方は、ぜひご参加ください。 Catoクラウド イベント・セミナー情報 | よくあるご質問 | Cato Cloud ケイトクラウド - SCSK 定期的にCatoクラウドをご紹介するセミナーを開催しております。・2024年9月26日(木)Catoクラウド ハンズオンセミナー【札幌開催分】 ~全国5都市開催(東京/大阪/名古屋/博多/札幌)~ ... cato-scsk.dga.jp SASEセミナー、Catoクラウドセミナー以外に、Catoクラウドのお客様導入事例の制作、 FAQサイト運営 、この  TechHarmony(技術ブログ) で、Catoクラウドの日本語のお役立ちサイトを目指し、皆様のお役に立て、少しでもCatoクラウド、SASEの知名度アップに貢献できればと考えておりますので、今後ともよろしくお願いします。 Catoクラウド エンジニアブログまとめ記事 Catoクラウドのエンジニアブログ(TechHarmony)でのまとめ記事となります。 blog.usize-tech.com 2024.02.27
アバター
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に参加するためには条件がありますが、参加できる条件にある方はぜひ立候補して参加を目指していただければと思います! 最後までお読みいただきありがとうございました!!
アバター