TECH PLAY

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

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

1141

SCSK LifeKeeper担当 池田です。 HAクラスタウェアには、様々な製品が巷にあふれています。 その中からどの製品を選定したら良いかという点は、皆さんの悩みどころかと思います。 SCSKでは様々なHAクラスタウェアの構築実績がありますが、その豊富な経験をもとに、いくつかのHAクラスタウェア製品とLifeKeeperとの機能の比較をしてみようと思います。 HAクラスタウェア製品の機能比較 HAクラスタウェア製品とLifeKeeperとを機能の違いで評価してみました。 機能 LifeKeeper A社製 B社製 備考 価格 – – – 構成により異なる為、記載を控えます 対応アプリケーション ◎ 主要ミドルウェア用有償オプション製品有。その他のミドルウェアもQSP(QuickServerProtection)やGenericARKで容易に対応可 〇 DBなど一部主要ミドルウェアに対応したオプション製品有。ただしほとんどがスクリプト作成が必要 △ スクリプト作成が必須   SLA 〇 99.99%のフォールトレジリエンス稼働レベル 〇 99.99%のフォールトレジリエンス稼働レベル 〇 99.99%のフォールトレジリエンス稼働レベル   構成の自由度 ◎ オンプレミス、仮想(vSphere)、パブリッククラウド対応のオプション製品有 △ 手組のスクリプト作成が必要 △ 手組のスクリプト作成が必要   対応OS ◎ Redhat Enterprise Linux Windows Server SUSE Linux Oracle Linux CentOS など多数 ◎ Redhat Enterprise Linux Windows Server SUSE Linux Oracle Linux CentOS Ubuntu など多数 △ 特定のOSに限定   サポート ◎ 24265 日本語サポート有 ◎ 24365日本語サポート有 △ 英語サポートのみ   運用 ◎ 管理GUIで表示が判りやすい 手動での系切り替えも用意 ◎ 管理GUIはあるが、慣れが必要 △ 管理GUIはあるが、どちらがActiveサーバなのか判りづらい   HAクラスタウェア製品は、20年以上前から存在していますので、製品によってそれ程大きな機能の差異はないのではと言われることが多いですが、上記の表のように実はLifeKeeperは優秀な製品なのです! 「SCSKはLifeKeeperを扱っているから評価が高いだけでしょ」という声も聞こえてきそうですが、冒頭にも書かせていただいた通り、SCSKではこれまで様々なHAクラスタウェアを設計、構築してきましたが、その経験から申し上げてもLifeKeeperは、非常にバランスの取れた製品であると感じています。 様々なアプリケーションに対応したオプション製品がメーカーから用意されていて、管理GUIの視認性が良く、どちらのサーバがアクティブになっているか、手動でスイッチオーバが必要な際にも、直感的な操作で実施できるため、運用性の高い製品となっています。 HAクラスタウェアを選定する際のご参考にしていただけると幸いです。 まとめ 今回は、HAクラスタウェアの製品比較をしてみました。大人の事情で製品名を書けず申し訳ありませんが、LifeKeeperの良さが少しでも伝わればと思います。 LifeKeeperについて、詳しい内容をお知りになりたいかたは、以下のバナーからSCSK LifeKeeper公式サイトまで
アバター
こんにちは、広野です。 AWS Cloud9 に代わる、使い勝手の良い IDE on AWS を作れないものか、、、日々模索しています。 今回は、 code-server を HTTPS 公開して MFA 認証を付ける構成を試してみました。まずはアーキテクチャを紹介します。実装は AWS CloudFormation でしており、別記事で紹介します。 背景 この構成を検討するにあたり、以下の状況がありました。 AWS Cloud9 のような Web IDE を AWS 上に作りたい。code-server を Amazon EC2 に立てるのがてっとり早い。個人用環境または研修用環境なのでコンテナのような本格的な構成は過剰。 code-server はデフォルトで HTTP (80) で公開され、ユーザー認証の仕組みは組み込みの機能が一応あります、って程度のもの。そのままでは実用的でない。 ALB や Amazon CloudFront をかぶせることで HTTPS 化はできるが、なんかこれだけのために使用するのは過剰すぎるという感覚。code-server をホストする Amazon EC2 インスタンス単体でどうにかしたい。 HTTPS 化は自己証明書は NG。自己証明書のサイトにはアクセスできないネットワーク環境があるため。ドメインは Amazon Route 53 パブリックホストゾーンを使用して登録し、証明書は Let’s Encrypt で作成することにする。 ユーザー認証は厳格にしたい。特に MFA は必須。Amazon Cognito を使用するのがてっとり早い。ログイン画面を開発するのは面倒なので、Amazon Cognito マネージドログインを全面的に活用する。 アーキテクチャ ということで、考えてみた構成がこちら。先に画面をイメージして欲しいので、解説は後ろに回します。   画面紹介 実際につくったものは、以下の操作になります。 作成した code-server インスタンスの URL にブラウザアクセスすると、OAuth2 Proxy の画面が表示されます。ボタンを押して進みます。 画面が Amazon Cognito マネージドログイン 画面にリダイレクトされます。画面デザインはデフォルトです。セルフサインアップも可能です。 MFA ワンタイムパスワードの入力も求められます。 ログインに成功すると、code-server が表示されます。 サインアウト用の URL は signout.md として用意しておきました。リンクを押すとサインアウトできます。ただし、今時点イケてないのはサインアウト後にログイン画面に戻ってくれないことです。いろいろ試しましたが一旦あきらめました。サインアウトはできているので。以下は Edge ブラウザでのサインアウト時画面。正常です。   アーキテクチャ解説 あらためて、アーキテクチャ図を掲載します。 なるべく処理順に説明します。 ユーザーは、ブラウザから HTTPS で code-server にアクセスしようとします。ドメイン名は Route 53 パブリックホストゾーンに登録されていて、Amazon EC2 インスタンスの Elastic IP アドレスが A レコードとして登録されています。そのレコードに合わせて、SSL 証明書が Let’s Encrypt で作成され、nginx に登録されています。 安全のため、Amazon EC2 インスタンスは任意のソースグローバル IP アドレス以外からの通信を受け付けないようにしています。 HTTPS 通信は nginx が受け取り、SSL の終端をします。以降、EC2 内部の通信は暗号化されません。 nginx は、ユーザーが認証済みかどうかチェックするために OAuth2 Proxy に問い合わせます。認証済みであればユーザーと code-server を通信させます。認証済みでない場合は、ユーザーに認証が必要である旨の画面を返します。 ログイン済みでないユーザーは、案内に従い Amazon Cognito マネージドログインの画面で認証 (MFA 込み) を済ませます。必要に応じてセルフサインアップをします。ログインが正常に完了すると、Amazon Cognito マネージドログインはあらかじめ指定されているコールバック URL (ここでは、OAuth2 Proxy の所定のパス) に画面をリダイレクトします。このとき、ブラウザは Cookie に認証済みコードを持った状態になっています。 認証済みとなったブラウザはリダイレクトされると、nginx 経由で OAuth2 Proxy が認証状況をチェックします。ここでは、認証済みコードからトークンを取得します。 細かい話ですが、アプリが Amazon Cognito ユーザープールに認証の通信をするためにはアプリケーションクライアントを作成する必要があります。今回の設計では、1 EC2 インスタンスあたり 1 アプリケーションクライアントの設定にしています。アプリケーションクライアントにはクライアントシークレットを持たせることができ、シークレットを知っているクライアントでないと認証をすることができません。シークレットは当該 EC2 インスタンスしか知らないようにしているので、他のデバイスが同じアプリケーションクライアント (≒ この EC2 インスタンス用 Amazon Cognito マネージドログイン画面) 経由で認証しようとしても拒否されます。正常に認証状況チェックが完了すると、EC2 インスタンスはトークンを取得できます。 上述、アプリケーションクライアントのクォータがあるため、デフォルトでは 1 Amazon Cognito ユーザープールあたり 1,000 アプリケーションクライアントを作成することができます。つまり、管理可能な code-server EC2 インスタンスは 1,000 台が上限となりますが、クォータの上限緩和申請や、設計を変えることで拡張する余地はあります。 ここまでの設計では、Amazon Cognito に正常に登録されたユーザーは全員、任意の code-server インスタンスにログインできてしまいます。今回の設計では code-server は個人用環境であるため、あらかじめ指定した自分のメールアドレス情報がユーザーの属性になければ認証を通さないようにします。Amazon Cognito ユーザープールのユーザー属性に email があるので、それを OAuth2 Proxy の認証状況チェックの過程でチェックさせます。もし指定されたメールアドレス以外の認証であれば、403 forbidden 画面が表示されます。 ログアウトについては、2箇所のサインアウト処理をする必要があります。OAuth2 Proxy と Amazon Cognito マネージドログインです。いずれもログアウトは所定の URL にアクセスさせる必要があり、1 アクションで両方に必ずアクセスさせるために URL リダイレクトを使用します。Amazon Cognito マネージドログインは必須でリダイレクト URL (ログアウト URL と呼ばれる) を指定する必要があるので、それを利用しました。ログアウト URL に、OAuth2 Proxy のログアウト用パスを指定するようにしています。ただし、それにアクセスしたときに前章で紹介したエラー画面が出てしまいます。   続編記事 作成次第、こちらに掲載します。   まとめ いかがでしたでしょうか? 今回の題材は code-server でしたが、他の認証機能のない (または弱い) Web アプリケーションによりセキュアな認証機能を付けたいニーズにぴったりはまると思います。 本記事が皆様のお役に立てれば幸いです。
アバター
こんにちは、ひるたんぬです。 最近は自分が食べたいと思ったものを、レシピを参考に作ることにハマっています。 レシピを見る際に、必ず「○人前」と書かれていますが、あれは何を基準としているのでしょうか? 調べてみても具体的な基準などは見当たらないのですが、大体乾麺のパスタだと、80〜100gというレシピが多い印象です。 ※ ご存知の方がいらっしゃいましたら、何らかの手段や媒体でご教示・発信いただけると幸いです。。。 私はこの量では満足することができないので、「私は人じゃない…?」と疑いかけますが、それは穿った見方ですね。 ただ、「一人前」と言う表現は個人差が大きいと思うので、もっと良い表現があってもいいのになぁ…と思った今日このごろです。 さて、今回はタイトルにもある通り、Amazon CloudFrontとAmazon S3を用いた静的コンテンツ配信基盤において、cognito-at-edgeで特定のパスにのみ認証を設定する方法をご紹介します。 やりたいこと 静的コンテンツを公開する場合、nginxやapacheを利用してWebサーバーを立てて公開する方法もありますが、クラウドネイティブな方法としてCloudFrontとS3を利用する方法が挙げられます。 これらを利用することにより、需要に応じたリソースのスケールや料金負担が実現できるほか、サーバー(インフラ)のメンテナンスを自身で行う必要がなくなるなど、コンテンツ配信の事業者の方々にとっても大きなメリットがあります。 一方、コンテンツの中には、特定のユーザーに公開を絞りたいケースもあるかと思います。 今回は、一つのサイト(ドメイン)の中で、全体に公開したいコンテンツと、公開を限定したいコンテンツを制御したいという事例を考えてみます。   使うもの 配信基盤として用いるものはAmazon CloudFrontとAmazon S3です。 また、認証にはAmazon Cognitoを利用し、CloudFrontと認証機能を連携させる手段としてLambda@Edgeを利用します。 具体的なLambda@Edge内の処理プログラムについては、AWS (AWS Labs)より提供されているcognito-at-edgeを利用します。 GitHub - awslabs/cognito-at-edge: Serverless authentication solution to protect your website or Amplify application Serverless authentication solution to protect your website or Amplify application - awslabs/cognito-at-edge github.com   事前準備・確認 まずは、特定のパスに限定させず、全てのコンテンツに対して公開を制限したいと思います。 コンテンツ格納用バケットの用意 コンテンツを格納するためのS3バケットを用意します。今回は東京リージョンにデプロイしたいと思います。 バケット名以外の設定はデフォルトで問題ありません。(今回は「202509-web-contents-bucket」を作成しました。) 作成が終わったら、何かしらコンテンツを格納しておきましょう。 こういうときに生成AIを使うとサクッと作れていいですね。 ChatGPTをはじめとする生成AIを「チャッピー」と呼ぶ界隈があることを最近知り、少し驚きました。 参考: 日本経済新聞社 | 「令和なコトバ「チャッピー」 わたしに寄り添う君の名は」 Cognitoのデプロイ Cognitoをデプロイします。今回は東京リージョンにデプロイしたいと思います。 CloudFormationより、以下のテンプレートをデプロイします。 パラメータのCognitoCallbackURLについては、ひとまずこのままでもOKです。 AWSTemplateFormatVersion: 2010-09-09 Description: Create Cognito. Please deploy at ap-northeast-1 region. # Parameters Parameters: ## CognitoコールバックURL CognitoCallbackURL: ### cognito-at-edgeの仕様上、最後は"/"をつけない Description: Cognito callback URL Type: String Default: https://example.com # Resources Resources: # Cognito関連リソース ## Cognito User Pool CognitoUserPool: Type: AWS::Cognito::UserPool Properties: UserPoolName: pathauth-cognitouserpool UserPoolTier: PLUS ### Cognitoのプラン DeletionProtection: INACTIVE ### 検証のため(削除できるように) UserPoolTags: Name: pathauth-cognitouserpool ## Cognito User Pool Client CognitoUserPoolClient: Type: AWS::Cognito::UserPoolClient Properties: ClientName: pathauth-cognitouserpool-client UserPoolId: !Ref CognitoUserPool GenerateSecret: false AllowedOAuthFlowsUserPoolClient: true AllowedOAuthScopes: - openid AllowedOAuthFlows: - code CallbackURLs: - !Ref CognitoCallbackURL SupportedIdentityProviders: - COGNITO ExplicitAuthFlows: - ALLOW_USER_SRP_AUTH ## Cognito User Pool Domain CognitoUserPoolDomain: Type: AWS::Cognito::UserPoolDomain Properties: UserPoolId: !Ref CognitoUserPool Domain: pathauth-userpool-domain ManagedLoginVersion: 2 ## Cognito Managed Login Page CognitoManagedLogin: Type: AWS::Cognito::ManagedLoginBranding Properties: UserPoolId: !Ref CognitoUserPool ClientId: !Ref CognitoUserPoolClient UseCognitoProvidedValues: true # Outputs Outputs: CognitoUserPoolId: Description: Cognito User Pool ID Value: !Ref CognitoUserPool CognitoUserPoolClientId: Description: Cognito User Pool Client ID Value: !Ref CognitoUserPoolClient CognitoUserPoolDomain: Description: Cognito User Pool Domain Value: !Sub "pathauth-userpool-domain.auth.${AWS::Region}.amazoncognito.com" Lambda@Edgeのコード準備 続いて、Lambda@Edgeにデプロイするためのcognito-at-edgeを準備します。 事前にバージニア北部リージョンに、完成したコードを格納するためのバケットを用意しておきます。バケット名以外の設定はデフォルトで問題ありません。(今回は「202509-cognito-at-edge-bucket」を作成しました。) 続いて、任意のコードエディタなどで、以下のファイルを作成し、「index.js」という名前で保存します。 userPoolId、userPoolAppId、userPoolDomainについては、先ほど作成したスタックの出力を参照してください。 const { Authenticator } = require('cognito-at-edge'); const authenticator = new Authenticator({ // Replace these parameter values with those of your own environment region: 'ap-northeast-1', // user pool region userPoolId: 'ap-northeast-1_abcdefgh1', // user pool ID userPoolAppId: '1examp1ec1ient1d', // user pool app client ID userPoolDomain: 'pathauth-userpool-domain.auth.ap-northeast-1.amazoncognito.com', // user pool domain }); exports.handler = async (request) => authenticator.handle(request); 作成が完了したらCloudShellで以下のコマンドを実行します。 [ ]内については適宜作業・ご自身の環境に置き換えてください。 [index.jsをアップロード] mkdir cognito-at-edge cd cognito-at-edge npm install cognito-at-edge mv ~/index.js ./ npx esbuild --bundle index.js --minify --outfile=bundle/index.js --platform=node cd bundle zip -r lambda-edge-auth.zip ./index.js aws s3 cp lambda-edge-auth.zip s3://[宛先S3バケット名]/lambda-edge-auth.zipさ 最終的にS3に「lambda-edge-auth.zip」というファイルがアップロードされていればOKです。 CloudFront・Lambda@Edgeのデプロイ Cognitoと同じようにCloudFormationでデプロイします。 バージニア北部リージョンでデプロイ するようにしてください。 パラメータについては、それぞれ作成したバケット名、Lambda@Edgeで用いるコードのファイル名(上記に従っていればlambda-edge-auth.zipになっているはずです。)を入力してください。 AWSTemplateFormatVersion: 2010-09-09 Description: Create CloudFront and Lambda@Edge. Please deploy at us-east-1 region. Transform: AWS::Serverless-2016-10-31 # Mappings Mappings: # CachePolicyIdは以下の記事を参照 # https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html CachePolicyIds: # Recommended for S3 CachingOptimized: Id: 658327ea-f89d-4fab-a63d-7e88639e58f6 # Parameters Parameters: # コンテンツの格納先(S3バケット名) OriginS3Bucket: Description: S3 bucket for contents Type: String Default: s3-bucket-name-for-contents # Lambda@Edgeのコード格納先(S3バケット名) LambdaEdgeCodeS3Bucket: Description: S3 bucket for Lambda@Edge code Type: String Default: s3-bucket-name-for-cognito-at-edge # Lambda@Edgeのコード格納先(S3キー名) LambdaEdgeCodeS3Key: Description: S3 key for Lambda@Edge code Type: String Default: lambda-edge-auth.zip # Resources Resources: # CloudFront周辺リソース ## CloudFront Distribution CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Origins: - Id: "S3Origin" DomainName: !Sub "${OriginS3Bucket}.s3.ap-northeast-1.amazonaws.com" S3OriginConfig: OriginAccessIdentity: "" OriginAccessControlId: !Ref CloudFrontOriginAccessControl DefaultCacheBehavior: TargetOriginId: "S3Origin" ViewerProtocolPolicy: redirect-to-https # Recommended for S3 CachePolicyId: !FindInMap [CachePolicyIds, CachingOptimized, Id] # cognito-at-edge LambdaFunctionAssociations: - EventType: viewer-request LambdaFunctionARN: !Ref LambdaEdgeFunctionForAuth.Version Enabled: true IPV6Enabled: false Tags: - Key: Name Value: !Sub "pathauth-cloudfront" ## CloudFront OriginAccessControl CloudFrontOriginAccessControl: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Description: "Origin Access Control For S3 Static Website Hosting" Name: !Sub "pathauth-s3oac" OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4 # Lambda@Edge周辺リソース ## Lambda@Edge Function for authentication LambdaEdgeFunctionForAuth: Type: AWS::Serverless::Function Properties: FunctionName: !Sub "pathauth-lambdaedge-auth" AutoPublishAlias: pathauth Handler: index.handler Runtime: nodejs22.x MemorySize: 128 Timeout: 5 CodeUri: Bucket: !Ref LambdaEdgeCodeS3Bucket Key: !Ref LambdaEdgeCodeS3Key Role: !GetAtt RoleForLambdaEdge.Arn ## CloudWatch Log Group for Lambda@Edge ### 実際の実行ログは各エッジロケーションに作成される LogGroupForLambdaEdge: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/lambda/${LambdaEdgeFunctionForAuth}" RetentionInDays: 30 ## IAM Role for Lambda@Edge RoleForLambdaEdge: Type: AWS::IAM::Role Properties: RoleName: cognito-at-edge-role AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com - edgelambda.amazonaws.com Action: sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - !Ref PolicyForLambdaEdge ## IAM Policy for Lambda@Edge PolicyForLambdaEdge: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: cognito-at-edge-policy Description: Policy for Lambda@Edge Function Path: / PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - iam:CreateServiceLinkedRole Resource: "*" - Effect: Allow Action: - lambda:GetFunction - lambda:EnableReplication Resource: ### Allow access to all Lambda functions in the account at the specified region(us-east-1) - !Sub "arn:aws:lambda:us-east-1:${AWS::AccountId}:function:*:*" - Effect: Allow Action: - cloudfront:UpdateDistribution Resource: ### Allow access to all CloudFront distributions in the account - !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/*" コンテンツバケットのバケットポリシー設定 コンテンツバケットにCloudFrontからのアクセス(OAC)を許可します。 まずは、CloudFrontの当該ディストリビューションを開き、「オリジン」タブを選択します。 「S3Origin」を選択し、「編集」を押下します。 画面中段にあるOACに関する設定項目から、「ポリシーをコピー」を押下し、その下にある「S3 バケットアクセス許可に移動」を押下します。 すると、コンテンツバケットのページが開くので、「アクセス許可」からバケットポリシーを変更します。 これにより、CloudFrontの指定ディストリビューションからオブジェクトの取得が許可されます。 CognitoのコールバックURL変更 今回は検証のため、CloudFrontのデフォルトドメイン(*.cloudfront.net)を使用します。 それに対応するよう、CognitoのコールバックURLを変更します。今回はコンソールより手動で変更します。 Cognitoのユーザープールを開いたら、「アプリケーションクライアント」から作成したアプリケーションクライアント(pathauth-cognitouserpool-client)を開きます。 次に、「ログインページ」タブを押下し、「マネージドログインページの設定」を編集します。 編集画面の一番上にある「許可されているコールバックURL」に、CloudFrontのURLを貼り付けます。 URLの最後に” / “をつけないようご注意ください。 変更が完了したら保存します。 動作確認 では、きちんと認証機能が働くか確認をします。 任意のWebブラウザを開き、CloudFrontのドメイン名と、表示させたいコンテンツのパス(例:examp1edoma1n.cloudfront.net.index.html)を入力しアクセスします。 きちんとサインインページに遷移しましたね。実際にユーザー名などを入力しサインインすると… コンテンツがしっかり表示されました。 今回は、あらかじめCognitoのユーザーをコンソールより作成しています。   検証 …ここからが本番です。 特定のパスのみ認証を設定していきます。今回は以下のようなフォルダ構成を仮定し、limited配下のみ公開に制限をかけます。 コンテンツバケット ├ limited/ │ ├ index.html │ └ style.css ├ index.html └ style.css CloudFrontディストリビューションのビヘイビア編集 CloudFrontのコンソール画面から「ビヘイビア」タブを選択し、「ビヘイビアの作成」を押下します。 パスパターンに制限をかけたいパスにアスタリスクを追加(/limited*)、オリジンには宛先のS3オリジン(S3Origin)を選択し、他の箇所はデフォルトで「Create behavior」を押下します。 次に、デフォルトビヘイビア(*)を編集します。編集画面の一番下、「関数の関連付け」を「関連付けなし」に設定します。 CognitoのコールバックURL編集 CognitoのコールバックURLをパスつきのURLに変更します。 詳細な手順は事前準備のときとほとんど同じなので省略しますが、以下のようになっていればOKです。 Lambda@Edgeのコード編集 Lambda@Edgeのコードを特定のパスに対してのみ認証できるよう変更します。 先ほど作成したindex.jsに一部追記をします。 const { Authenticator } = require('cognito-at-edge'); const authenticator = new Authenticator({ // Replace these parameter values with those of your own environment region: 'ap-northeast-1', // user pool region userPoolId: 'ap-northeast-1_abcdefgh1', // user pool ID userPoolAppId: '1examp1ec1ient1d', // user pool app client ID userPoolDomain: 'pathauth-userpool-domain.auth.ap-northeast-1.amazoncognito.com', // user pool domain parseAuthPath: '/limited', // 追記① cookiePath: '/limited' // 追記② }); exports.handler = async (request) => authenticator.handle(request); 追記が終わったら先程と同じようにCloudShellからzip化し、S3にアップロードします。名前は変えておくと分かりやすいかと思います。今回は「 lambda-edge-auth-path.zip 」としました。 Lambda@Edgeの更新 作成したコードでLambda@Edgeを更新します。今回はコンソールより更新します。 まず、先程アップロードしたS3バケットから、当該コンテンツのオブジェクトURLをコピーします。 次にLambdaのコンソールから、認証に用いているLambda(pathauth-lambdaedge-auth)を探し開きます。 コード編集画面の右上にある「アップロード元」から「Amazon S3の場所」を選択し、先程コピーしたURLを貼り付けます。 関数の更新が完了したら、Lambda@Edgeにデプロイします。 右上の「アクション」から「Lambda@Edgeへのデプロイ」を選択します。 ディストリビューションは作成したもの、キャッシュ動作には先程作成したビヘイビア(/limited*)を選択します。CloudFrontイベントについては「ビューアーリクエスト」に変更してください。 最後に一番下の「Lambda@Edge へのデプロイを確認」に✅️を入れてデプロイをします。 以上で変更作業は完了です。 動作確認 早速想定の挙動になるか見ていきます。 まずは、公開されているコンテンツ(ルートのindex.html)にアクセスします。 問題なく表示されました。認証もありません。 では、次に限定コンテンツ(/limited/index.html)にアクセスします。 きちんと認証画面に移りましたね!良かったです。認証情報を入れてサインインしてみると… いかにも特別感のあるページが表示されました。ありがとう、チャッピー。 メンバー数やコンテンツ数などそれっぽく出ていますが、中身は空っぽです。   終わりに 事前準備が少し多かったので長くなってしまいましたが、パスごとの認証は思ったよりもシンプルな手順でできたので良かったです。 本記事の内容が、どなたかの参考になることを願っております。 余談ですが、先日生まれて初めてオイルマッサージなるものを体験してきました。 担当の方に「全身ボロボロですね…」と遠回しに言われ少し傷ついたので、折を見て通おうかなと思ったりしています。
アバター
はじめに こんにちは。SCSKの末本です。 2025年8月5日、6日に東京ビッグサイトで開催された、Google Cloudが主催する日本最大のクラウドイベント「Google Cloud Next Tokyo ’25」に参加してきました。 専門家でなくても誰もが当たり前のようにAIを使いこなし、ビジネスや日々の業務を加速させる、そんな未来がすぐそこまで来ていることを実感した一日でした。 本記事では、イベントの概要から特に印象に残ったセッションについてレポートします。 ※弊社も登壇およびブース出展しておりましたが、本レポートでは割愛いたします。 Google Cloud Next Tokyo 25  とは、開発者から経営者までクラウドテクノロジーに関心のある幅広い層の方々を対象とした年に一度のイベントです。 今年は「AI for everyone」をテーマに150を超えるセッションや展示ブースが用意され、会場は多くの参加者の熱気に包まれていました。Googleの最新テクノロジーに触れられるだけでなく、業界をリードする企業の導入事例や、Googleのエンジニアと直接交流できる貴重な機会となっています。 ▼会場 (東京ビッグサイト)      なぜ今、Google Cloud なのか? 生成AIの登場により、私たちの働き方は大きな変革期を迎えています。 日々の情報収集、資料作成、アイデア出しといった業務をもっと効率化し、より創造的な仕事に時間を使いたい・・・そう考えたとき、Googleが提供するGeminiをはじめとしたAIソリューションは、最も身近で強力な選択肢の一つです。 今回のNext Tokyoでは「業務効率化」「生産性向上」を自分のテーマとして、最先端のAI活用法を学び、自分の業務にどう落とし込むかのヒントを得たいと考えて会場へ向かいました。   DAY2 イベントレポート 数多くのセッションの中から、個人的に興味のあるテーマ(業務効率化・生産性向上)の観点で、特に学びが深かった2つのセッションをレポートします。 もう手放せない!Gemini の NotebookLM、Deep Research、Canvas で思考を加速 AIの進化がビジネスの風景を塗り替える中、Googleの最先端AIモデルである Gemini は、単なる情報検索やコンテンツ生成を超え、私たちの「考える」プロセスそのものを変革する可能性を秘めています。 本セッションでは、ビジネスパーソン、研究者、開発者、そしてあらゆる分野の意思決定者が直面する複雑な課題に対し、Gemini の革新的な機能群である Deep Research、NotebookLM、そして Canvas を活用した新しい情報活用ワークフローが紹介されました。 Deep Research あるテーマについて、Web上の膨大な情報から信頼性の高いソースを引用しつつ、多角的な視点を提供してくれるリサーチツールです。複雑な調べものにかかる情報収集の時間と手間を大幅に短縮し、質の高いインプットを可能にします。 NotebookLM ユーザが提供した情報源(ソース)に基づいて応答する、AIを活用したリサーチ兼ライティングアシスタントです。 ドキュメントやウェブサイトのURLを アップロードすると、 NotebookLMはその情報だけを学習データとして扱います。 これにより、インターネット上の不確かな情報に影響されず、手元にある資料に基づいた正確な要約の作成、専門的な質問への回答、アイデアの創出などが可能になり、 リサーチや学習の精度、スピードを飛躍的に高めてくれます。 また学習ソースが限定されておりハルシネーションが起きにくいこと、さらに音声出力が可能という特徴もあります。 Canvas 与えられた情報について整理し、文章やコードをアウトプットします。生成された内容を手軽に修正できるため、Geminiと対話しながら思考を整理・発展させることができ、コンテンツの質を高めていくことができます。 これらの機能は単体でも強力ですが、組み合わせて活用することで、情報収集からアウトプットまでの 一連のワークフローを劇的に変える力を持っています。 Gemini で変革!投資家のためのニュース革命 〜第 3 回生成 AI Innovation Awards 最優秀賞〜 こちらは、生成AIの具体的なビジネス活用事例として非常に興味深いセッションでした。 生成AI Innovation Awards は、Google Cloudが主催する、生成AI技術の活用を通じてビジネスの課題解決や革新的なアプローチを実現した事例を表彰するイベントです。 企業の生成AIを活用した優れた取り組みを発掘し、広く共有することを目的としており、 生成AIの進化とともに注目度が高まっています。 第3回で最優秀賞を受賞したこの事例では、株式会社SBI証券がAlpacaTech株式会社と開発した、 投資情報サービス「朝刊・夕刊」 が紹介されました。 Geminiを活用して、投資家向けのニュース分析サービスを構築 したというものです。   日々配信される膨大な経済ニュースや決算情報を、人間がすべて追いかけるのは不可能です。 このサービスでは、Geminiがリアルタイムでニュースを分析し、 「注目度」や「重要度」でスコアリング 。好材料・悪材料となる銘柄を自動で選定し、当日の日本株の見通し・相場を投資初心者にも分かりやすいように要約したまとめ記事を提供します。 この事例の核心は、 人間では不可能な規模の情報処理をAIに任せ、人間はより高度な分析と最終判断に集中する という役割分担を明確にしている点だと思いました。 これは金融業界に限った話ではなく、あらゆる業界でビジネスの質とスピードを両立するために活用していけるのではと思いました。 参考: 第3回 Google Cloud 生成 AI Innovation Awards 最終審査 & 結果発表 最後に 一日を通して、非常に多くの刺激と学びを得ることができました。 私自身は生成AIの活用方法を模索中ですが、 「AIと働く」スタイルを実践し、 日々の業務をより効率化していきたいです。 最後までお読みいただきありがとうございました!
アバター
こんにちは。SCSK星です。 ServiceNowの更新セットについて書きたいと思います。 最近更新セットを使用していなかったお客様に「更新セットについて教えてほしい」とご依頼いただきまして、 折角なのでその時の資料を使ってまとめておこうと思います。   更新セットとは? 更新セットは英語環境では「Update Sets」と表記されます。 イメージとしては「他環境へ作成データや更新データを移行するための箱のようなもの」になります。 更新セットを用いてリリースを行うことで、複数の更新データを一度に反映できる、2つの環境でSysIDが揃うなどのメリットがあります。 開発環境に反映した作成データや更新データを更新セットに格納し、移送先環境へ適用することで変更作業が完了いたします。   更新 セット の使い方 更新セット初心者向けに少し細かく手順を記載していこうと思います。 更新セットの移送方法をもっとわかりやすく、簡潔に知りたい方は記事をご参考ください。 https://blog.usize-tech.com/servicenow-updateset-guide/ 開発環境での手順 まず開発環境で作業を行います 1.メニューから システムアップデートセット>ローカル更新セット を選択します。 2.作成した更新セットのリストが表示されますので、右上の新規ボタンを押下します。 3.作成する更新セットの名前を付けます。(名前に日付や説明欄に詳細などを入れるとわかりやすいです) 4.名前付けしたら「送信して選択」を押下します。 5.画面上部のバナーにある地球儀のようなマークから更新セットを選択していることを確認いたします。 6.更新セットが選択された状態(バナーの地球儀マークが赤い状態)で移行先環境へ移行したいデータの作成・更新を行います。 ※更新セット選択中は更新データが自動的に記録されます。更新セットを選択した状態で関係のない作業を行わないでください。 7.移行したいデータの作成・更新が完了したら、更新セットを外します。(バナーの地球儀マークから更新セットを「Default」に変更するなど) 8.メニューから システムアップデートセット>ローカル更新セット を選択します。 9.移行したいデータの作成・更新が完了した更新セットを開き、ステータスを「完了」に変更し保存する。 10.ステータスを「完了」にし保存すると、下部の関連リンクに「XMLにエクスポート」というリンクができるので押下する。 11.XMLファイルがダウンロードされるので、移行先環境適用に向けて保存しておく。 (sys_remote_update_set…という名前でダウンロードされます。わかりやすいようにファイル名の変更は可能です。) 移行先環境での手順 XMLファイルの作成が完了したら移行先環境で作業を行います 1.メニューから システムアップデートセット>取得済み更新セット を選択します。 2.移行先環境へリリースした更新セットのリストが表示されますが、リスト下部にある関連リンクまでスクロールし「XML から更新セットをインポート」を押下する。 3.XMLのインポート画面に遷移するので、「ファイルの選択」を押下する。開発環境で作成しエクスポートしていた    XMLファイルを選択して「アップロード」を押下する。 4.アップロードが完了するとステータスが「ロード完了」の状態で更新セットが追加される。 ※この状態ではまだ開発環境で行った変更は反映されておりません。 5.追加した更新セットを開き、「更新セットのプレビュー」を押下する。 ※プレビューを行ったときに、エラーや警告が出力される場合がある。 エラーが出力された場合はそれらをレビューする。(後述:注意点⑤) 6.プレビューが完了したら「更新セットのコミット」を押下する。 ※コミットが完了すると更新セットに入れていた作成・更新データが移行環境へ反映されます。     更新セットを使うときの注意点 注意点①:全ての作成データ・更新データが更新セットに格納されるわけではない 開発環境で行った 更新の全てが更新セットに保存されるわけではありません 。 【更新セットに格納されないデータ】 ・データレコードの作成や更新は更新セットに格納されません。 例:インシデントレコードの新規作成、編集。サービスカタログでの要求の作成、編集など ・ユーザやグループの作成や更新 開発環境で作成したユーザやグループの情報は更新セットに格納されません。 【その他追加されないデータ】 ・タスク ・変更されたCI ・スケジュール ・スケジュール設定済みジョブ ・ホームページ これらを移行したい場合は個別にXMLエクスポートする必要があります。 注意点 ②: 更新セットを作成したバージョンと更新セットを適用する バージョンが同一である必要がある。 更新セットを作成した時の開発環境バージョンとリリースするときの移行先環境の バージョンが一致している必要 があります。 バージョンアップでデータを退避するために更新セットを使用する場合はご注意ください。     注意点③: 1つの更新セットに更新データをたくさん入れることは非推奨。 1つの更新セットに全ての更新データを格納しリリースするとわかりやすくてよいですが、1つの更新セットに格納する更新データは「50~100件程度」までが推奨されてます。100件を超える場合は更新セットを分割し開発・リリースする必要があります。 更新データが100件を超えてしまうとパフォーマンス低下やマージ失敗につながる可能性がある ことがServiceNow公式から周知されています。   注意点④:更新 データを更新セットに格納するときや、更新セットをリリースするときは適用順序を意識する必要がある。 注意点③で記載した通り、更新データが100件を超えるようなリリースする場合は、更新セットは複数個にすることが推奨されています。 これらを移行先環境へリリースする際に適用順序を意識する必要があります。 依存するデータの リリース順を間違えると、プレビューの際にエラーが発生 します。 こちらの回避策は後述の「バッチ機能について」をご覧ください。 注意点⑤: 基本的に更新セットを プレビューした際は エラー が 発生 する 。 多数のデータを格納してリリースすると、気を付けていてもエラーは発生するものとご認識ください。 エラーの基本的な対応を記載します。 対処方法についてはこちらもご参考ください→ リモート更新セットのプレビュー まずプレビューの際に発生したエラーの「説明」でどのようなエラーか把握します。 (※画像の例は更新セット内の更新より新しい更新が既に移行先環境にある場合に出るエラーです) 次に「使用可能なアクション」でどのような更新なのかを判断し、更新セットの内容を適用する/適用しないを選択します。 ○ローカルと比較 現在の環境内データ(ローカル)と更新セットのデータ(リモート)を比較することができます。 ○ローカルのレコードを表示 現在の環境内の該当データを表示することができます。 ○ローカルの更新を表示 現在の環境内の該当データをXML形式で確認することができます。 これらの機能を使用し、どのような更新か、リリースすべきか保持すべきかを判断します。 判断後は以下のどちらかを選択することでエラーを取り除くことができます。 ○リモートの更新を承認 更新セットのデータを適用いたします。 ○リモートの更新をスキップ 更新セットのデータを適用せずに、現在の設定を維持します。 「リモートの更新」「リモートの更新をスキップ」を承認間違って押下してしまってもコミットするまでは環境に反映されません。 誤って押下してしまった場合は「プレビューの再実行」ボタンで再びエラーを表示させてください。   バッチ機能について バッチ機能は複数の更新セットをひとまとまりにして疑似的に1つの更新セットとして扱うことができる機能です。 注意点④で説明したリリース順を間違えると、にエラーが発生する問題を解決することができます。 バッチの適用方法 1.開発環境でメニューから システムアップデートセット>ローカル更新セット を選択します。 2.親とする更新セットを新規作成、またはバッチとしてまとめたい更新セットの中から一つ決定します。 3.親の更新セットも含めバッチとしてまとめたい更新セットのステータスを全て「完了」に変更します。 4.バッチとしてまとめたい更新セットの一つを開きます。 5.更新セットの「親」フィールドを選択し、親の更新セットを選択します。 6.他の更新セットの「親」フィールドにも同じ親の更新セットを設定し、バッチとしてまとめたい更新セットのすべてが一つの共通の親を持つように設定します。 7.親として設定した更新セットを開きます。 8.関連リンクの「更新セットバッチをXMLにエクスポート」を押下することで、子の更新セットのデータも含めたデータをXMLファイルとしてダウンロードします。 9.「移行先環境での手順」と同様の手順で移行先環境へダウンロードしたXMLファイルを適用します。 ※「結合」という機能はバッチとは異なります。機能的には類似しておりますがバッチの方が新しい機能であり推奨されております。   終わりに こちらはあくまで更新セットの簡単な説明および使用手順になります。 更新セットのより詳しい説明はServiceNow公式ドキュメントをご覧ください。
アバター
皆さま、はじめまして。2025新人のkhaleedと申します。 新人研修も終盤に差し掛かっており、配属後の業務についていけるか日々不安を感じながらも、学びの機会を大切に過ごしています。 本業はセキュリティ関連の部署に携わる予定ですが、自己研鑽の一環としてAWS(Amazon Web Services) にも触れてみています。AWSには非常に多くのサービスがあり、調べれば調べるほど奥が深く、「これは一体どんな場面で使うんだろう?」と疑問に思うことも少なくありません。 そこで今回は、機密情報を安全に管理できる「AWS Secrets Manager」について、自分なりに調べた内容をまとめてみました。まだ勉強中の身ではありますが、同じようにAWSを学び始めた方にとって少しでも参考になれば幸いです。 AWS Secrets Managerとは 今回、勉強しようと思った背景は、DBにおいて認証情報を自動でローテーションしてくれるサービスとして、AWS Secrets Managerが紹介されていたことです。 Managerっていうからには、何かを自動化してくれるマネージドサービスなのかな? SecretsをManageするんだから、機密情報を守ってくれる的な。といったイメージを勉強前はもっていました。 概要 AWS Secrets Managerとは、各種AWSサービスやオンプレミスへのアクセスに必要な機密情報を管理してくれるサービスです。 具体的には、 認証情報(ID、パスワード) APIキー OAuthトークン(動的な認証情報) etc… といった機密情報を管理、取得、ローテーションしてくれます。また、誰がどの機密情報にアクセスしたかのログも取得することができます。 続いて、認証情報の自動ローテーションについても追加で調査しました。 AWS Secrets Managerを用いた認証情報の自動ローテーション 今回は例として、データベースの認証情報をローテーションする例を考えます。 1. 認証情報を登録 まず、接続対象となるDB、DBユーザー名、DBパスワードなど各種認証情報を登録します。 2. 自動ローテーションを有効化 続いて自動ローテーションを有効化します。この時、ローテーション期間も設定することができます。 ローテーションの期限になると、後述するLambda関数が呼び出され、自動的に認証情報が変更されます。 3. Lambda関数を登録 最後に、このシークレットにLambda関数をアタッチします。 Lambda関数なんて難しくて書けないよ!という方も、指定すればAWS側が自動で関数を作成してくれるみたいです。   実際にやってみた 文字面だけだとほんとにできるのか不安だったので、実際にやってみました。 DBはこちら↓ 認証情報を登録 自動ローテーションを有効化 & Lambda関数登録 結果 ローテーション前↓ ローテーション後↓ パスワードがしっかり変更されているのが分かります。 デフォルトのLambda関数だとタイムアウトが30秒と短すぎて変更する前にエラー吐いちゃうので、手動でタイムアウトを1分30秒とかにすると良さそうです。   勉強してみて 機密情報を自動でローテーションしてくれるのは、アクセス管理の手間が省けて便利そう。 これからも手を動かして検証できたら嬉しいです。   参考文献 What is AWS Secrets Manager? – AWS Secrets Manager Amazon RDS、Amazon Aurora、Amazon DocumentDB、Amazon Redshift のシークレットで自動ローテーションを設定にする – AWS Secrets Manager
アバター
皆さんこんにちは 気が付いたら誕生日まで残り一か月、どうも。いとさんです。 誕生日といえば、ホールケーキですね。 小さいときは丸ごと一つ食べるのが夢で、中学生の時に一度だけですが小さいホールケーキを丸ごと一つ買ってもらいました。 当時は運動部だったので余裕で食べきった記憶があります。今はアフタヌーンティーでおなかいっぱいです さて今回はAWS Lambdaを使ってAWS Security Hub CSPMの検出結果を日本語で通知する方法について調査・実装してみました。 はじめに 従来、本サイトの運用ではAWS Security Hub CSPMで検出があった際にをAmazon EventBridge ⇒ Amazon SNS経由でメールで通知しておりました。 特に加工していなかったこともあり、以下のように検出結果がJSON形式で送られてきていました。 今回は、この通知を日本語でわかりやすく、かつ通知が完了したらNOTIFED(確認済み)にするように構築していこうと思います。   アーキテクチャ図 設定方法 前提として AWSアカウントが有効 Security Hub CSPMが有効化済み 通知先メールアドレスが決まっている IAMユーザに十分な権限があること(Amazon SNS、Amazon Translate, Amazon EventBridgeの呼び出し) 上記の内容が設定されていることが必要です。 使用するIAMユーザーに十分な権限が設定されていな場合は、 IAM>ロール>ロールの作成から、 ユースケースをAWS Lambdaに設定し必要な権限を付与、ロールを作成します。   1. SNSトピックの作成 AWSコンソール → SNS → トピック → 「新しいトピックを作成」 名称例: securityhub-notify-jp この時タイプはスタンダードに設定してください(サブスクリプションをEメールで設定するため) 作成されたトピックのarnを控えます(例:arn:aws:sns:ap-northeast-1:123456789012:securityhub-notify-jp) 2. サブスクリプションの作成 SNSが送られる方法を設定します。 サブスクリプション>サブスクリプションの作成 先ほど控えたトピックのarnを選択しプロトコルをEメールで設定します。 エンドポイントは送信先に設定したいメールアドレスを記入します。 以下のメールが送られてくるので『Confirm subscription』をクリックしステータスを確認済みにします。   3. Lambda関数(日本語化+SNS Publish)の作成 Lambda関数の設定を行います。 以下のように設定します。 関数名:(例:SecurityHubJapaneseNotify) ランタイム: Python3.13 実行ロール: 既存のロールを使用するを選択し、SNS Publish、Translate権限のあるLambda関数用のIAMロールを選択します コード例:  import boto3 import os from datetime import datetime def lambda_handler(event, context): sns = boto3.client('sns') translate = boto3.client('translate') securityhub = boto3.client('securityhub') findings = event['detail'].get('findings', []) print("取得したfindings:", findings) if not findings: print("No findings detected.") return {'status': 'No findings'} for finding in findings: print("Processing finding:", finding.get('Id', 'No Id')) if finding.get('Workflow', {}).get('Status') != 'NEW': print("Skipping finding (not NEW):", finding.get('Id', 'No Id')) continue # Workflow Statusを NOTIFIED に変更 sh_response = securityhub.batch_update_findings( FindingIdentifiers=[ { 'Id': finding['Id'], 'ProductArn': finding['ProductArn'] } ], Workflow={'Status': 'NOTIFIED'} ) print("SecurityHub update response:", sh_response) # 2つ目の処理: 詳細な情報を含むSNS通知 (CRITICAL/HIGHのみ) en_severity = finding.get('FindingProviderFields', {}).get('Severity', {}).get('Label', 'UNKNOWN') if en_severity in ['CRITICAL', 'HIGH']: en_title = finding.get('Title', 'No Title') en_desc = finding.get('Description', 'No Description') en_region = event.get('region', '不明') en_account = event.get('account', '不明') en_time = finding.get('FirstObservedAt', event.get('time', '不明')) en_arn = finding.get('ProductFields', {}).get('Resources:0/Id', '不明') remediation = finding.get('Remediation', {}).get('Recommendation', {}).get('Text', '') jp_title = translate.translate_text(Text=en_title, SourceLanguageCode="en", TargetLanguageCode="ja")['TranslatedText'] jp_desc = translate.translate_text(Text=en_desc, SourceLanguageCode="en", TargetLanguageCode="ja")['TranslatedText'] jp_severity = '重大' if en_severity == 'CRITICAL' else '高' try: jp_time = datetime.strptime(en_time[:19], "%Y-%m-%dT%H:%M:%S").strftime("%Y年%m月%d日 %H時%M分%S秒") except Exception: jp_time = en_time jp_remediation = translate.translate_text(Text=remediation, SourceLanguageCode="en", TargetLanguageCode="ja")['TranslatedText'] if remediation else "対応情報なし" message_detailed = f"""【SecurityHub検出通知】 検出タイトル: {jp_title} 説明: {jp_desc} 重大度: {jp_severity} 発生時間: {jp_time} リージョン: {en_region} AWSアカウント: {en_account} 関連リソースARN: {en_arn} 推奨対応: {jp_remediation} -- この通知はAWS Security Hubにより自動送信されました。 """ sns_response_detailed = sns.publish( TopicArn="トピックのARN", # 適切なARNに置き換えてください Subject="SecurityHub検出通知", Message=message_detailed ) print("SNSへ日本語通知を配信しました\n", message_detailed) else: print(f"Severity {en_severity} is not CRITICAL or HIGH. Skipping detailed notification.") print("All findings processed.") return {'status': 'Complete'}  細かい指定の際にtypoが出来ないように気をつけます。 4. EventBridgeルールの設定 AWSコンソール → Amazon EventBridge → ルール → 「ルールを作成」します 名前例: SecurityHubFindingsJPNotify イベントバス:デフォルト イベントパターンを作成します ターゲットタイプ:「Lambda関数」  コード例           { "source": ["aws.securityhub"], "detail-type": ["Security Hub Findings - Imported"], "detail": { "findings": { "Compliance": { "Status": ["FAILED", "WARNING"] }, "RecordState": ["ACTIVE"], "Workflow": { "Status": ["NEW"] }, "Severity": { "Label": ["HIGH", "CRITICAL", "MEDIUM", "LOW", "INFORMATIONAL"] } } } } ターゲットを選択します ターゲットを選択:Lambda関数 ターゲットの場所:このアカウントのターゲット 関数:先ほど作成したLambda関数 実行ロール:SecurityHub CSPMの検出結果を日本語化するために作成したロールを選択 タグは必要に応じて設定します。 レビューと作成を行いルールの作成を押下します。 5. 動作テスト・確認 該当Lambda関数へ移動し 以下のコードを使いテストします。 { "version": "0", "id": "test-id-12345", "detail-type": "Security Hub Findings - Imported", "source": "aws.securityhub", "account": "123456789012", "region": "ap-northeast-1", "time": "2025-09-04T05:58:40Z", "resources": [ "arn:aws:securityhub:ap-northeast-1:123456789012:product/aws/securityhub/arn:aws:securityhub:ap-northeast-1:security-control/EC2.8/finding/finding-id-1" ], "detail": { "findings": [ { "Id": "finding-id-1", "ProductArn": "arn:aws:securityhub:ap-northeast-1:123456789012:product/aws/securityhub", "Title": "Instance Metadata Service is not required.", "Description": "HttpTokens is set to optional.", "Workflow": { "Status": "NEW" }, "FindingProviderFields": { "Severity": { "Label": "HIGH", "Original": "HIGH" } }, "ProductFields": { "Resources:0/Id": "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-0123456789abcdef0" }, "FirstObservedAt": "2025-09-04T05:57:40.923Z", "Remediation": { "Recommendation": { "Text": "Review instance metadata options and update configuration.", "Url": "https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-controls.html" } } } ] } } テストを押下し成功すると無事以下のメールが届きました。 メールに付属している以下の2つのリンクはサブスクリプション解除と問い合わせのリンクなので間違えて踏まないようにしましょう (チームメンバーが悲しくなります) 失敗するとエラーが起きるので該当箇所を修正します。 後日確認してみると セキュリティの都合上他の範囲をお見せすることは出来ませんが SecurityHubの検証結果が確認作業を行わなくても確認済みになっておりました。(メールは2回目以降のものが多く送られてこなかった)   まとめ 初めてAWS Lambda関数やAmazon Eventbridgeを使いましたが、仕組みを理解すると意外と簡単に構築できました。 細かく出力情報を指定する場合はTypoがないかを都度テストし確認することが大事です。 次はサブスクリプション解除のリンクの踏み間違いを防ぐ方法について調査してみようと思います。 ではでは(@^^)/~~~
アバター
こんにちは。SCSKの雪山です。 8/5(火)~8/6(水)に東京ビッグサイトで開催された「 Google Cloud Next Tokyo ’25 」に参加しました!(Day2のみ) 私はこれまでGoogle Cloudを利用したことがなく、、、そのため初心者目線でイベントの概要・感想などをまとめてみました。 利用されている方からすると当たり前の内容も含まれるかもしれませんが、ご容赦ください。 基調講演 事前のセッション登録画面では満席になっていたため立ち見覚悟でしたが、席は先着順だったようで無事座ることができました。 基調講演では、AIを中心としたさまざまなサービスの進化や、各企業の活用事例・実践デモなどが紹介されました。 AIの需要は8年間で約1億倍 になっているとのこと。AIは現代において不可欠なものになっていると改めて感じさせられました。 また、セキュリティについての紹介では、「AIで守る、AIを守る」という言葉がありました。 AIは日々進化していますが、そのAIを利用することなどで攻撃者の技術もより巧妙になっています。Google CloudではAIを用いた様々なセキュリティサービスを提供しており、これらを利用することでシステムを「AIで守る」ことが可能です。また、今回の講演では、モデルの盗難やAIシステムへの脅威を防ぐ「AI Protection」という機能も紹介され、これからは「 AIを守る 」という視点も重要と感じました。 参考: AI Protection の発表: AI 時代のセキュリティ セッション いくつかセッションに参加しましたので、ざっくり内容をご紹介します。 (事前登録がイベント開催間近になってしまい、人気のセッションはすでに満席になっていました。。) 我々は、生成AIアプリを開発するべきなのか 多種多様なAIのサービスが提供されている昨今、自社で生成アプリを開発すべきなのか、というお題のセッションでした。 前半はGoogle Cloudが提供しているAIソリューションの簡単な紹介でした。(Gemini,Gems,NotebookLM,Vertex AI,Google AI Studio,ADK,Model Armor,BigQuery ML…などなど)今や当たり前なのかもしれませんが、多種多様なツールが提供されているなと感じました。 で、本題への回答は、結局 「ケースバイケース」 とのこと。 単純な業務改善が目的:できあいのサービスを利用すべき 時間をかけて作りこんでも、Googleがそれを上回る製品をリリースすることも考えられ、無駄になってしまう可能性があります。また、上述した様々なAIソリューションもあり、既製品で十分対応可能なケースも想定されます。 自社特有のロジックや独自機能との連携が必要:開発を検討 まずは既製品で対応可能か検討し、NGだった場合に開発を検討すべき、とのことでした。Geminiアプリの精度が足りない場合や、より複雑かつ多段な作業をさせたい、セキュリティ要件が厳しい、などのケースが該当します。 要は既製品で可能なことに加え、要件やユースケースをちゃんと整理・検討したうえで開発要否を判断すべき、ということですね。 様々なソリューションが提供されているため、多くの場合は既製品で対応可能ではないかと個人的には思いますが、単純な業務改善でない場合は結局セキュリティ面や独自機能との連携の要件があったりで、開発するという判断になる場合が多いのかなと思いました。 AI時代に必要なセキュリティマネジメントスキル クラウドセキュリティにおいて、Google Cloudの最新のセキュリティプロダクトの紹介と、AIを利用した効率的で安全なセキュリティマネジメントの実現方法についてのセッションでした。 クラウドが主流となりつつある今、AIアシスタントの進化・自律型AIエージェントの登場により、マネジメント業務についても変革が進んでいます。セキュリティマネジメントにもAIをうまく活用すれば、より効率的で安全なマネジメントが可能になります。 ただ、すべてのマネジメントをAIに任せるべきか、というとそうではありません。意思決定や柔軟性など、マネジメントにおいて人間が担うべき領域もあります。そのため、 AIと人間でそれぞれ得意とする領域ごとにうまく役割を分担する 、ということが大切です。 上記のような協働モデルとすることで、24時間稼働の実現・分析・解析スピードの向上・単純作業の自動化などのメリットが生まれます。 一方、この協働モデルには以下のような課題もあります。 AIの情報が常に正しいとは限らず、不正確性を孕んでいる AIが情報を持っていない独自ノウハウや暗黙知が存在する可能性がある 重大な報告相談などのコミュニケーションが減少する可能性がある 1,2はもちろんのこと、3も身近な課題だと感じました。問題に直面し行き詰まってしまった際、最近だと「誰かに相談する」ではなく「まずはAIに質問してみる」という方も多いと思います。そうした場合、「AIとのやり取りだけで自己完結」→「重要な情報共有がされない」→「情報共有不足による二次災害リスク増」という事態につながる可能性があります。AIの積極的な利用は対人コミュニケーションの減少にもつながるため、上記1,2も含め利用者がそういったリスクを理解・意識することが重要だと改めて認識しました。 セッションの後半に、Google Cloudが提供しているセキュリティ管理サービスの紹介がありましたので簡単に紹介します。 Security Command Center :Google Cloud環境全体のセキュリティを一元的に管理・監視するための統合セキュリティプラットフォーム。構成ミスや脆弱性、脅威を自動的に検出し、対応を支援します。また、最上位のティアでは、AWSやAzureなど、他のクラウド環境のセキュリティ情報も統合して監視・管理することも可能です。 Google Security Operations SIEM :あらゆるログデータをリアルタイムで収集、一元管理、分析し、潜在的なサイバー攻撃や脅威を検出するためのソリューションです。 Google Threat Intelligence :Googleが保有する世界最大級の脅威情報や、セキュリティ企業Mandiantの知見を活用し、攻撃者の全体像把握と防御策強化を支援するサービスです。 Google Security Operations SOAR :SIEMが発報したアラート(警告)に対する調査と対応プロセスを自動化・効率化するためのソリューションです。 ブース ブースもいくつか周ったので簡単にご紹介します。 AIバスケットボールコーチ 会場の中央に大きな施設があり、中を見てみるとバスケットボールのコートになっていました。 こちらは「AIバスケットボールコーチ」というもので、シュートを打つとその時の姿勢やボールの起動を分析し、AIがコーチングしてくれる、というものです。 別の方の結果ですが、スコアとともに以下のようなコーチングが表示されていました。想像していたよりも詳細な評価で驚きました。 最近はAIをスポーツの場面で活用する、という動きが活発になってきている印象を受けます。テレビで放送されているスポーツ中継などでもすでに利用されていますが、学校の部活など、より身近なところで利用され始めるのもそう遠くないのかもしれません。 Red Hat 業務でRed Hat社のOpenShiftを利用しているのもあり、雑談のような感じで少し話を聞きました。 AWSのROSA(Red Hat OpenShift Service on AWS)と同等のサービスが、Google Cloudでも「 OpenShift Dedicated 」として提供されていることを、恥ずかしながらこの場で初めて知りました。ただできることはほぼ同じだそうで、既存のクラウド環境や利用したいサービスに応じて選択すべし、とのことでした。 その他、VMWareからの脱却の話などもしましたが、、、ここでは割愛します。 (赤い帽子が当たるくじもあり挑戦しましたがハズレ。ハンカチをもらいました。) Google Workspace活用術 かなりの人だかりができていたので話を聞いてみました。 ANAの開発工場のDX事例の紹介で、従来の紙ベースの運用を、Google Workspaceを利用した電子データベースの運用に切り替えた、という内容でした。 担当の方はITについての知識が全くなかったところから、明示クッカー社のYoutubeで勉強を重ね、自身が主導となってGoogle Workspaceでシステムを構築・移行したそうです。この事例で苦労した点、工夫した点などを対談形式で紹介されていました。 一番苦労したのは、「 利用者の理解を得る こと 」だったそうです。 良くある話だと思いますが、現状のシステムを変えるとなると、組織や現場の理解を得ることは難しいです。特に今回は紙から電子データへの移行ということで、利用者から不満や疑問の声がかなりあったそうです。 こちらの担当の方は、焦らず根気よく会話し、現場も巻き込みながら、「なぜ変えるのか」「何が変わるのか」「どうすればいいのか」といった点を現場の人も含めて説明し続け、かつ何度もハンズオンを実施したそうです。その結果利用者の理解も得つつ移行が実現でき、今では必要不可欠なシステムになっているとのことでした。当たり前のように感じますが、実際にやるのは苦労するんだろうなと思います。 また、Google Cloudはデフォルトで様々な機能を提供しているとのこと。使いこなせば特に機能を追加することなくやりたいことが実現できるそうです。 感想・余談 これまでGoogle Cloudを利用したことがなかったため、イベントに参加しても内容を理解できるか不安でしたが、セッションではサービスの概要から説明してくれるものも多く、割と理解できた気がします。もちろん一部サービス名などは調べないと「?」でしたが、AWSとほぼ同等の機能を提供するものも多く、「これはAWSで言うところのこのサービスか」となるのも大きかったと思います。 軽く調べたところ、Google Cloudの強みはデータ分析とAI・機械学習だそうです。また、上記のGoogle WorkspacesなどのSaaSとの連携性も高いとのことで、これらを利用するのであればGoogle Cloudを選択するのがいいのかもしれません。(詳しい方、その他特長などあれば教えてください) 来年の開催日は 2026/7/30(木)~2026/7/31(金) です!Google Cloudを触ったことがない方でも、新たな知見だったり刺激を得られる場になっているので、興味のある方はぜひ参加してみてください。 以下、余談です。 ・休憩スペースに出店がありました。(チュロスとブリトー)休憩スペースには机と椅子がありましたが、常に満席でした。。 ・当日は酷暑にもかかわらず大盛況だったと思います。(今年の来場者数はまだ発表されていないようでした)
アバター
こんにちは SCSK 野口です。 前回の記事 『LifeKeeper の Quorum/Witness (Storage) を導入してみた』 では、 Quorum/Witness(Storage)を実際に導入してみました。 まだ、お読みでない方は上記リンクからご覧ください。 本記事では、Quorum/Witness(Majority)を仮想マシン上のLinux環境に導入してみます。 おさらい 前々回の記事でもお伝えしましたが、Majority構成は多数決判定をするため、 ノードを奇数用意する必要があります。そのため、Majority構成は 3ノード以上から動作可能となってます。 また、majorityモードで使用可能な Witnessモードは以下となります。 ・remote_verify ・none または off QuorumとWitnessの構成が正しくないと、サーバが誤って停止するんだったよね!   導入 今回の構成について 今回も Virtual Box (仮想VM) を使用し、3台のサーバ (ノード) 構成にします。 前回の Active/Standby構成のクラスターに Witnessサーバを追加することによって、多数決を実現します。 3ノード以上の奇数台構成のクラスターであれば、追加のノードを用意する必要はありません。 Majority構成では、Quorum チェックを実行した後に、Witness チェックを実行します。 専用の Witnessノード は、 複数のクラスターで共有することができるわよ! でも、複数のクラスターで同時に障害が発生したら、 通常の構成よりも フェイルオーバーに時間がかかってしまうわ。 だから、専用のWitnessノードは1つのクラスターだけで使用することをお勧めしてるんだね!   動作環境 今回の Quorum/Witness (Majority)を導入した構成は以下となります。   前回の Active/Standby構成に Witnessサーバを導入するわ! 導入する際、Witnessサーバ間でもコミュニケーションパスを作成しなきゃだね! 導入手順 1.Witnessサーバをセットアップし、他のサーバとネットワーク通信ができることを確認します。 ※ サーバセットアップの詳細は省かせていただきます。 新たに追加したWitnessサーバ間とも通信できているか、pingコマンドで疎通確認してみます。 [root@rhel75wit ~]# ping -c 3 rhel75n01 PING rhel75n01 (192.168.56.130) 56(84) bytes of data. 64 bytes from rhel75n01 (192.168.56.130): icmp_seq=1 ttl=64 time=1.13 ms 64 bytes from rhel75n01 (192.168.56.130): icmp_seq=2 ttl=64 time=2.61 ms 64 bytes from rhel75n01 (192.168.56.130): icmp_seq=3 ttl=64 time=1.37 ms --- rhel75n01 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2004ms rtt min/avg/max/mdev = 1.134/1.707/2.618/0.651 ms [root@rhel75wit ~]# ping -c 3 rhel75n02 PING rhel75n02 (192.168.56.140) 56(84) bytes of data. 64 bytes from rhel75n02 (192.168.56.140): icmp_seq=1 ttl=64 time=0.747 ms 64 bytes from rhel75n02 (192.168.56.140): icmp_seq=2 ttl=64 time=2.91 ms 64 bytes from rhel75n02 (192.168.56.140): icmp_seq=3 ttl=64 time=1.67 ms --- rhel75n02 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 0.747/1.779/2.914/0.888 ms 2.Witnessサーバにも LifeKeeperをインストールします。 その際、「Use Quorum/Witness functions」を有効にし、Quorum/Wintness パッケージをインストールします。 3.Witnessサーバも含めた、すべてのノード間でコミュケーションパスを作成し、ALIVEであることを確認します。 LKWMCの場合は、左タスクバーの「コミュニケーションパス」を選択します。 コミュニケーションパスのステータスが「ALIVE」であることを確認します。 4.すべてのノードで Quorum/Witnessの設定を行います。(/etc/default/LifeKeeper) 初めに、1号機と2号機を下記のように設定していきます。 次に、Witnessサーバを下記のように設定します。 上記の手順が完了すると、そのクラスターでは Quorum/Witness 機能が動作するようになり、 フェイルオーバーが許可される前に、Quorum チェックおよび Witness チェックが行われます。 前回の Quorum Witness (Storage) と違い、初期化コマンドなどはありません。 Witnessノード は リソースを持たない から、 Quorumチェック と Witnessチェックを実施する必要はないわ!   だから、QUORUM_MODE と WITNESS_MODEを「none」にしてたんだね!   動作確認 1号機 (rhel75n01) 2号機 (rhel75n02) 間のコミュニケーションパスを切断しても Witnessサーバの監視により「お互いが見えている状態か」、実際に確認してみます。 今回、1,2号機間のコミュニケーションパスで利用しているネットワークインターフェース 「 enp0s8(192.168.58.130)」を ifdownコマンドで無効化(シャットダウン)します。 [root@rhel75n01 ~]# ifdown enp0s8 デバイス 'enp0s8' が正常に切断されました。 ifdownコマンドを実行後、「リソースツリー」画面を確認すると、このように表示されます。 コミュニケーションパス (192.168.58.130)のステータスが「DEAD」であることを確認します。 1号機 (rhel75n01) 2号機 (rhel75n02)のログを確認すると、 お互いがサービスを継続していることが分かるメッセージがあります。 Aug 29 18:30:05 rhel75n01 lifekeeper[14514]: NOTIFY:event.comm_down:::010469:We do have quorum on comm_down to rhel75n02, continuing Aug 29 18:30:02 rhel75n02 lifekeeper[10132]: NOTIFY:event.comm_down:::010469:We do have quorum on comm_down to rhel75n01, continuing 最後に 1号機で lcdstatus -eコマンドを実行し、仮想IPリソースが「ISP(正常稼働)」であることを確認します。   まとめ 今回は Quorum/Witness(Majority) を実際に導入してみましたがいかがでしたか。 導入する際は、以下の点に注意してください。 ・導入する前に、ノード数が3以上で奇数になっていることを確認する。 ・導入する際は、Witnessノード含めたコミュニケーションパスを作成し、 Aliveであることを確認する。 ・動作確認では、1,2号機間のコミュパスに障害を起こしても、問題なく稼働し続けることを確認する。  
アバター
クラウドって便利だけど油断すると怖いですね。 会社の検証環境でリソースを構築していたところ、気づいたら想定外の料金が発生していました。 原因は、完全に私の知識不足です。何のリソースにどのくらいの料金かかるのか把握せず、好き勝手に作ってたら請求来てしまいました。 結果として、無駄にしてしまった金額は約6万円。会社のお金です。 私が実際にやらかしてしまった無駄に費用がかかったAzureリソースを3つを紹介しつつ、同じ失敗を繰り返さないためのポイントも共有したいと思います。 クラウド初心者の方や、検証環境を扱うエンジニアの方に、少しでも参考になれば幸いです。 Azure Bastion リソースの概要 Azure Bastionは、VMへのRDPやSSH接続を安全に提供するサービスです。 VMの外部公開を避けるために使ってました。 料金 Bastionは、利用中かどうかに関わらず“リソースがある限り”毎時間課金されます。               https://azure.microsoft.com/ja-jp/pricing/details/azure-bastion/ azure.microsoft.com Azure Bastion Basicの料金は約¥28円/時間ですので、一日で約674円、一月で約20,227円かかります。 私がこの料金体系把握してなかった頃に放置してたため、6月頃に19,922円の費用を発生させてしまいました。             対策は、「使い終わったらすぐ消す!!使いたくなったらまた作る!!」です。 Bastionは1分もかからず再作成できるので、節約しましょうねという話でした。   Azure Functions リソースの概要 Azure Functionsはイベント駆動型でコードを必要な時だけ実行できるサーバレスのFaaSです。 同じようなリソースだとAWS Lambdaです。 料金 Functions作成時には、ホスティングオプション選びます。 関数アプリをどのようなインフラ上で実行・スケーリング・課金するかを選ぶためのオプションです。 Azure Functions のスケールとホスティング Azure Functions で関数アプリを実行するホスティング プランを選択するときに考慮する必要があるさまざまなオプションを比較します。 learn.microsoft.com 普通は使った分だけ料金がかかる従量課金プランを選ぶべきなのかもしれませんが、私はインスタンスを持っているだけで課金があるPremiumを選んでしまいました。 本番環境がPremiumだったので検証も同じで設定してしまったからなのですが、Premiumが何なのか理解して使うべきでした。 私が設定したEP1の料金はポータルから見れて、30円/時間掛かります。       放置してた2つのFunctionsで8,400円無駄にしてました。 アプリを実行する検証をしたわけではないので、これは本当に無駄にしてしまいました、、             対策は従量課金プランにすること、もしくはBastionと同じで使い終わったらすぐ消すことです。 さらに存在してるだけでお金かかるプランの存在を理解することが大事です。   Azure マネージドディスク リソースの概要 Azure マネージドディスクは、VMに付ける仮想ディスクストレージです。 CドライブとかEドライブのために使ってました。 料金 ディスクが一番お金を使ってしまいました。 私が使っていたサイズがP15で、以下料金表です。(ディスク選択の際にとりあえず本番と同じサイズを選んでしまいました。)                 https://azure.microsoft.com/ja-jp/pricing/details/managed-disks/ azure.microsoft.com 月額約6,464円です。今回問題なのが、作成したディスクが1つではないことです。災対用にVMのリストア検証を何度もやっていたら、そのたびにP15ディスクがどんどん増えてしまって…。 増やしたり消したりを繰り返して、結果3か月で37,097円の費用になりました。           対策は安いディスクを付ければよいだけです。 検証では大量のデータを保存する必要はないので、ディスクのサイズは関係ないことを理解してなかったです。   さいごに 料金体系を理解して節約してれば、もっと安く検証できたなと反省してます。 3000円くらいに抑えられたのかも。 再発防止に努めます。
アバター
こんにちは。SCSKの南です。 パブリッククラウドの利用は今や多くの企業にとって欠かせないものとなり、同時に「クラウドセキュリティ」をどう確保するかは経営上の大きな課題になっています。 クラウドセキュリティについて調べていく中で、きっとすでに 「CNAPP(Cloud-Native Application Protection Platform)」 という言葉を耳にしたことがあるのではないでしょうか。ベンダー各社が「CNAPP対応」の製品を打ち出して、クラウドセキュリティ市場では一種のバズワードとなっています。 しかし、「CNAPP対応製品を導入すれば万事解決」なのでしょうか? 実際にはそう単純な話ではありませんので、その辺りをご説明したいと思います。 CNAPPとは何か CNAPPとはそもそも「機能」ではなく「概念」です。ガートナーは下記のように定義しています。 Cloud-native application protection platforms (CNAPPs) are a unified and tightly integrated set of security and compliance capabilities, designed to protect cloud-native infrastructure and applications. CNAPPs incorporate an integrated set of proactive and reactive security capabilities, including artifact scanning, security guardrails, configuration and compliance management, risk detection and prioritization, and behavioral  analytics, providing visibility, governance and control from code creation to production runtime. CNAPP solutions use a combination of API integrations with leading cloud platform providers, continuous integration/continuous development (CI/CD) pipeline integrations, and agent and agentless workload integration to offer combined development and runtime security coverage. (日本語訳:自動翻訳) クラウドネイティブアプリケーション保護プラットフォーム(CNAPP)は、クラウドネイティブなインフラストラクチャとアプリケーションを保護するために設計された、一体化された密接に統合されたセキュリティおよびコンプライアンス機能のセットです。CNAPPは、アーティファクトスキャン、セキュリティガードレール、構成およびコンプライアンス管理、リスク検出および優先順位付け、行動分析などの包括的なプロアクティブおよびリアクティブなセキュリティ機能を統合し、コード作成から本番実行までの可視性、ガバナンス、コントロールを提供します。CNAPPソリューションは、主要なクラウドプラットフォームプロバイダーとのAPI統合、継続的インテグレーション/継続的開発(CI/CD)パイプラインの統合、エージェントとエージェントレスのワークロード統合を組み合わせて、開発と実行のセキュリティカバレッジを提供します。 概念なので定義をぱっと読むとだいぶ範囲が広くて、わかりずらいのでポイントを整理しました。 CNAPPの重要な要素 # 要素 概要 1 統合プラットフォーム バラバラに存在していたクラウドセキュリティ機能をひとつにまとめた仕組み 例:CSPM(設定管理)、CWPP(ワークロード保護)、CIEM(権限管理)などを統合 2 対象範囲 クラウドネイティブなインフラとアプリケーション全体を保護 例:IaaS、PaaS、Kubernetes、コンテナ、サーバレス環境など 3 プロアクティブ(予防的)機能 設定・コンプライアンス管理(CSPM的な要素) セキュリティガードレール(開発・運用時の自動チェック) コードやコンテナイメージのスキャン(Artifact Scanning) 4 リアクティブ(事後対応)機能 リスク検出と優先度付け 振る舞い分析(異常な挙動の検知、脅威ハンティング) 5 ライフサイクル全体をカバー コード作成 → ビルド → デプロイ → 本番稼働まで DevSecOps/シフトレフトの考え方を実現 6 提供する価値 可視性(何が起きているか見える) ガバナンス(ポリシーに従わせる) 制御(必要に応じて止める・修正する) 上の表を見るといろんな要素があり、また要素ごとにセキュリティの観点が違ったりするのでCNAPP製品を入れただけであまり意味がないことが分かっていただけるかなと思います。 CNAPPはどのようにはじめるべきか CNAPPは前述したとおり「バラバラに存在していたクラウドセキュリティ機能をひとつにまとめた仕組み」になりますので、機能ごとにセキュリティを考えるとわかりやすいです。 CNAPPに含まれる機能 CNAPPに含まれる機能を一覧にしてみました。 ※「CNAPPにはこの機能が含まれる」といった明確な定義はありません。さまざまな資料で「主要コンポーネント」や「一般的に含まれるべき機能群」が明示されたりしていますが、新しい機能が次から次に追加されている状況です。 # 区分 モジュール 説明 1 コア セキュリティ CSPM (Cloud Security Posture Management) クラウド環境の設定ミスやコンプライアンス違反を検出・修正。 2 CIEM (Cloud Infrastructure Entitlement Management) IAM権限の過剰付与や不適切なアクセス制御を検出し、最小権限化を推進。 3 CWPP (Cloud Workload Protection Platform) VM・コンテナ・Kubernetesなどのワークロードを保護。脆弱性、マルウェア、実行時の防御。 4 高度セキュリティ IaC Security Terraform、CloudFormationなどIaCテンプレートのセキュリティスキャン。 5 DSPM (Data Security Posture Management) クラウド上のデータを分類・可視化し、機密情報のリスクを管理。 6 KSPM (Kubernetes Security Posture Management) Kubernetesクラスタ特有のセキュリティ設定・ポリシーを管理。CSPMの一部とも見なされるが独立性も強い。 7 ASPM (Application Security Posture Management) アプリケーションレベルで脆弱性やリスクを可視化・管理。ソフトウェアサプライチェーン対策とも重なる。 8   AI-SPM (AI Security Posture Management) AIモデルやAIワークロードのセキュリティを保護(データ汚染やモデル悪用対策)。新しい領域。 9 CI/CDセキュリティ統合 DevSecOpsを実現。コードスキャン、アーティファクト検査を開発パイプラインに統合。 10 Runtime Threat Detection 実行環境での異常挙動検知(脅威ハンティング、行動分析)。 11 Security Guardrails  開発者や運用者が意識せずに安全に利用できるようにする自動チェック・制御。 導入する順番は? たくさんの機能があることがわかると思いますが、まず最初にやるべきなのは「 CSPM 」と「 CIEM 」です。「設定」と「権限」という土台の部分を固めることが最優先だと思います。これらの機能はすべての環境に共通で必要なセキュリティになります。導入をすることで、すべての環境のセキュリティを向上させることができます。 また、CSPMを導入することで、クラウド環境の可視化(どのサービスが稼働しているのか、どこが外部に公開されているのか、どの部分が規制やガイドラインに抵触しているのか、等)も実現できるため、セキュリティ強化を行う際にどの環境に何のセキュリティを導入すべきかを検討する材料にもなります。 次は「 CWPP 」です。クラウド上にあるワークロードを保護することが重要です。オンプレ端末(PCやサーバ)のセキュリティは一生懸命検討されるのですが、クラウド上のワークロード保護は放置されているケースが多いです。 それから「高度なセキュリティ」の機能群を導入するのが良いと思います。「高度なセキュリティ」の機能は環境によって必要かどうかが変わってくる部分がありますので、どのように展開するかなど環境に合わせて検討する必要があります。 クラウドセキュリティの出発点は「CSPM」 最近は「CNAPP」という言葉を耳にする機会が増えています。いろいろなセキュリティ機能をまとめて守ってくれる“全部入り”の仕組みとして紹介されることが多く、とても魅力的に感じられます。 ですが、実際にクラウドで起きているトラブルの多くは「人が設定を間違えた」ことが原因です。たとえば、本来は社内だけで使うはずのストレージを「公開」にしてしまったり、必要以上に広い権限をユーザーに与えてしまったり…。こうしたちょっとしたミスから、大きな情報漏えいにつながってしまうケースが後を絶ちません。 そこでまず必要になるのがCSPMです。CSPMはクラウドの設定が正しいかどうかをチェックし、危ないところを教えてくれる“健康診断”のような役割を持っています。これがあることで、クラウド全体を見渡せるようになり、「どこにリスクがあるのか」を把握できるようになります。 つまり、CNAPPのような大きな仕組みを使うにしても、最初の一歩はCSPMで“安全の基礎”を整えることです。建物でいえば、どんなに立派な高層ビルを建てても、土台がしっかりしていなければ崩れてしまうのと同じです。クラウドセキュリティも、まずはCSPMという“土台”を固めることが何より大切だと思います。 どのようなソリューションを選ぶべきか パルアルトネットワークス社のPrisma CloudのようなCNAPPに対応した包括的なセキュリティプラットフォーム製品を導入することが推奨です。Prisma CloudはCNAPP領域のリーダーとして評価されているソリューション・製品となります。 先ほど説明したようにCNAPPには複数の機能がありますが、セキュリティプラットフォーム製品であれば段階的にセキュティを強化することができます。 単独の製品を導入すると、ツールのサイロ化や、統合的な分析ができないといった状況になってしまうため、この領域はプラットフォーム製品を導入するのが良いと考えます。 さいごに、当社ではクラウド診断サービスやマネージドCSPMサービスを提供していますので、ご興味のある方は是非、お気軽にお問い合わせください。 Smart One Cloud Security® パブリッククラウドのセキュリティ設定を診断/監視するマネージドCSPMサービスです。Palo Alto Networks社Prisma Cloud(CSPM機能)を使い易く、簡単に導入いただけます。 www.scsk.jp マルチクラウド設定診断サービス with CSPM| SCSK株式会社 マルチクラウド環境のセキュリティ設定リスクを手軽に確認可能なスポット診断サービスです。独自の診断レポートが、運用上の設定ミスや設計不備、クラウド環境の仕様変更などで発生し得る問題を可視化し、セキュリティインシデントの早期発見に役立ちます。 www.scsk.jp  
アバター
こんにちは、広野です。 AWS Cloud9 亡き後の IDE 環境をいろいろと考えてまして。結局のところシンプルな構成に落ち着きました。作成した構成を紹介します。自分の PC に VSCode をインストールしている人には全く役に立たない記事ですのでご放念ください。 ざっくり要件 AWS Cloud9 の代わりとなる、クラウド上の IDE を構築したい。 リモートアクセスはブラウザで (HTTPS で) で通信できるようにしたい。(特殊なポートは NG) HTTPS 通信で自己証明書を使用するのは NG。 OS は Windows 以外にしたい。ライセンス面や価格面など、いろいろと。 アーキテクチャ VSCode をインストールした Ubuntu 24.04 arm64 にブラウザから GUI アクセスする環境です。 EC2 には Elastic IP アドレスを割り当て、それをあらかじめ用意してある Amazon Route 53 パブリックホストゾーンに A レコード登録します。そのドメインおよびサブドメインに合わせて、Let’s Encrypt で SSL 証明書を作成します。証明書の有効期限は 90 日なので、更新するためのスクリプトを EC2 内に作成します。(自動更新ではなく、必要になったら実行) リモートアクセス機能は Amazon DCV が提供します。ただし 8443 ポートを使用するので、一応そのポートはそのまま利用できるようにしておき、8443 にアクセスできない環境のために 443 ポートでもアクセスできるよう、nginx でリバースプロキシを構築します。 SSL 証明書は、Amazon DCV および nginx にカスタム証明書として組み込みます。 誰でもアクセスできるのはまずいので、とりあえず指定したソース IP アドレスでないと許可しないようセキュリティグループを設定します。セキュリティ面は引き続き強化を検討します。研修用途であれば現状で問題ないと思います。 要件から考えたこと AWS Cloud9 の代わりに、クラウド上の IDE を使用したい。 やはり VSCode もしくは VSCode 準拠のツールにしたいです。まずは純正 VSCode 使用で試行錯誤しています。そのため Amazon EC2 上で動く環境を作りました。今後は code-server も検証したいです。 リモートアクセスはブラウザで (HTTPS で) で通信できるようにしたい。(特殊なポートは NG) RDP や VNC ではなく、ブラウザでリモートのデスクトップを操作できる Amazon DCV を採用しました。会社のネットワーク通信の制約で、特殊ポートの通信ができない場合も 443 であれば通過できます。 HTTPS 通信で自己証明書を使用するのは NG。 会社のネットワークセキュリティ等で、自己証明書の Web サイトにはアクセスできないことがあり、正規の SSL 証明書を用意する必要がありました。EC2 に ALB や Amazon CloudFront をかぶせることも考えましたが、コスト面を気にしたのと、CloudFront だと自己証明書のオリジンをサポートしていないために結局オリジン (EC2) に正規の証明書を実装せねばならず、採用をやめました。 OS は Windows 以外にしたい。 OS が Windows であれば AWS Systems Manager Fleet Manager によるリモートアクセスはできたのですが、Windows だと高額だったり、会社のルールでセキュリティ対策などしないといけないので、Linux にしました。Linux の中でも、AWS で馴染み深い Amazon Linux 2023 を使用したかったのですが、本来サーバー用途での利用を想定された OS なのでデスクトップが使いにくく、次に馴染みのある Ubuntu にしました。Mint も考えましたが、純正 AMI が無かったのでやめました。インスタンスタイプは Graviton (arm64) を使用した t4g の方が t3 (x86_64) よりも若干安かったので、t4g を採用しています。幸い、使用するツールも arm64 に対応してました。 AWS CloudFormation テンプレート 補足はインラインコメントします。 EC2 ユーザーデータの中で以下のツールをインストールしているのですが、必要に応じて追加、削除しましょう。 VSCode Amazon DCV nginx AWS CLI Amazon Q Developer CLI (サインインが必要なのでインストーラダウンロードのみ) Let’s Encrypt 証明書作成用モジュール NVM (Node.js バージョン管理ツール) AWSTemplateFormatVersion: "2010-09-09" Description: The CloudFormation template that creates Ubuntu 24.04 ARM64 EC2 instance with GNOME 3, VSCode, and Amazon DCV. # ------------------------------------------------------------# # Input Parameters # ------------------------------------------------------------# Parameters: SystemName: Type: String Description: System name. use lower case only. (e.g. example) Default: example MaxLength: 10 MinLength: 1 SubName: Type: String Description: System sub name. use lower case only. (e.g. prod or dev) Default: dev MaxLength: 10 MinLength: 1 DomainName: Type: String Description: Domain name for URL. xxxxx.xxx Default: example.com MaxLength: 40 MinLength: 5 # VPC は既存にあるものを使用する想定です。 VpcId: Type: AWS::EC2::VPC::Id Description: Choose a existing VPC ID you deploy the EC2 instance in. InstanceSubnet: Type: AWS::EC2::Subnet::Id Description: Choose an existing Public Subnet ID you deploy the EC2 instance in. InstanceType: Type: String Description: EC2 instance type (ARM/Graviton) Default: t4g.large AllowedValues: - t4g.large - t4g.xlarge InstanceVolumeSize: Type: Number Description: The volume size in GB Default: 20 # アクセスを許可するソースIPアドレス(サブネット)です。 AllowedSubnet: Type: String Description: Allowed source IPv4 subnet and subnet mask. (e.g. xxx.xxx.xxx.xxx/32) Default: xxx.xxx.xxx.xxx/32 MaxLength: 18 MinLength: 9 TimeZone: Type: String Description: The specified time zone. Default: Asia/Tokyo MaxLength: 30 MinLength: 1 # ubuntu ユーザーの初期パスワードです。Amazon DCV のパスワードにもなります。リソース作成後は変更しましょう。 InitialPassword: Type: String Description: The initial OS password. You must change it after your first login. Default: Gx8jeTLc MaxLength: 128 MinLength: 8 # Route 53 パブリックホストゾーンは既存で持っている想定です。 Route53HostZoneId: Type: String Description: Route 53 public host zone ID for the SSL certificate. Default: XXXXXXXXXXXXXXXXXXXXX MaxLength: 30 MinLength: 1 # Let's Encrypt は証明書作成時にメールアドレスが必要になります。 YourEmail: Type: String Description: Your email address for SSL certificate application. Default: xxx@xxx.xxx MaxLength: 30 MinLength: 1 Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "General Configuration" Parameters: - SystemName - SubName - Label: default: "Domain Configuration" Parameters: - DomainName - Route53HostZoneId - YourEmail - Label: default: "Network Configuration" Parameters: - VpcId - InstanceSubnet - AllowedSubnet - Label: default: "EC2 Configuration" Parameters: - InstanceType - InstanceVolumeSize - Label: default: "OS Configuration" Parameters: - TimeZone - InitialPassword Resources: # ------------------------------------------------------------# # EC2 Launch template # ------------------------------------------------------------# EC2LaunchTemplate: Type: AWS::EC2::LaunchTemplate Properties: LaunchTemplateName: !Sub vscode-${SystemName}-${SubName} LaunchTemplateData: InstanceType: !Ref InstanceType ImageId: >- {{resolve:ssm:/aws/service/canonical/ubuntu/server/24.04/stable/current/arm64/hvm/ebs-gp3/ami-id}} BlockDeviceMappings: - Ebs: VolumeSize: !Ref InstanceVolumeSize VolumeType: gp3 DeleteOnTermination: true Encrypted: true DeviceName: /dev/sda1 NetworkInterfaces: - SubnetId: !Ref InstanceSubnet Groups: - !Ref Ec2SecurityGroup DeviceIndex: 0 AssociatePublicIpAddress: false MetadataOptions: HttpTokens: required Monitoring: Enabled: true TagSpecifications: - ResourceType: volume Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} DependsOn: - Ec2SecurityGroup # ------------------------------------------------------------# # EC2 Security Group # ------------------------------------------------------------# Ec2SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref VpcId GroupDescription: Allow dcv-server access from specified subnets SecurityGroupIngress: - Description: Allow HTTPS (8443) from the specified client FromPort: 8443 IpProtocol: tcp ToPort: 8443 CidrIp: !Ref AllowedSubnet - Description: Allow QUIC from the specified client FromPort: 8443 IpProtocol: udp ToPort: 8443 CidrIp: !Ref AllowedSubnet - Description: Allow HTTPS from the specified client FromPort: 443 IpProtocol: tcp ToPort: 443 CidrIp: !Ref AllowedSubnet SecurityGroupEgress: - Description: Allow all outbound traffic IpProtocol: -1 CidrIp: 0.0.0.0/0 Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub SG-vscode-${SystemName}-${SubName} # ------------------------------------------------------------# # EC2 Role / Instance Profile (IAM) # ------------------------------------------------------------# Ec2Role: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: CodeWhisperer requires '*' as a resource, reference https://docs.aws.amazon.com/codewhisperer/latest/userguide/cloud9-setup.html#codewhisperer-IAM-policies Properties: RoleName: !Sub Ec2Role-vscode-${SystemName}-${SubName} Description: This role allows EC2 instance to invoke S3 and SSM. AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: / # EC2 インスタンスには、とりあえず私が必要だった権限しか付与していません。用途に応じて追加が必要です。 ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore - arn:aws:iam::aws:policy/AWSCodeCommitPowerUser - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy Policies: - PolicyName: !Sub Ec2Policy-vscode-${SystemName}-${SubName} PolicyDocument: Version: 2012-10-17 Statement: # Amazon DCV のライセンスを読み取るために必要な権限です。   - Effect: Allow Action: s3:GetObject Resource: !Sub arn:aws:s3:::dcv-license.${AWS::Region}/* # Amazon Q Developer 関連 - Effect: Allow Action: - codewhisperer:GenerateRecommendations Resource: "*" # 以下は Let's Encrypt への証明書申請のために必要です。DNSによるドメイン所有者確認をします。 - Effect: Allow Action: - route53:ListHostedZones - route53:GetChange Resource: - "*" - Effect: Allow Action: - route53:ChangeResourceRecordSets Resource: - !Sub arn:aws:route53:::hostedzone/${Route53HostZoneId} Ec2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Ref Ec2Role Path: / Roles: - !Ref Ec2Role DependsOn: - Ec2Role # ------------------------------------------------------------# # Elastic IP address for EC2 instance # ------------------------------------------------------------# EipEc2: Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub eip-vscode-${SystemName}-${SubName} # ------------------------------------------------------------# # EIP Association # ------------------------------------------------------------# EIPAssociation: Type: AWS::EC2::EIPAssociation Properties: AllocationId: !GetAtt EipEc2.AllocationId InstanceId: !Ref Ec2Instance DependsOn: - Ec2Instance - EipEc2 # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# Ec2Instance: Type: AWS::EC2::Instance Properties: IamInstanceProfile: !Ref Ec2InstanceProfile LaunchTemplate: LaunchTemplateId: !Ref EC2LaunchTemplate Version: !GetAtt EC2LaunchTemplate.LatestVersionNumber UserData: Fn::Base64: !Sub | #!/bin/bash set -euxo pipefail export DEBIAN_FRONTEND=noninteractive cd /root # User password echo "ubuntu:${InitialPassword}" | chpasswd # Locale & timezone timedatectl set-timezone ${TimeZone} apt update -y apt upgrade -y apt install -y language-pack-ja-base language-pack-ja ibus-mozc unzip update-locale LANG=ja_JP.UTF-8 sed -i 's/^XKBLAYOUT=".*"/XKBLAYOUT="jp"/' /etc/default/keyboard dpkg-reconfigure -phigh console-setup systemctl restart console-setup # AWS CLI curl https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip -o awscliv2.zip unzip awscliv2.zip ./aws/install rm awscliv2.zip rm -fr aws # ubuntu desktop apt install -y ubuntu-desktop gdm3 xserver-xorg-video-dummy mesa-utils sed -i -e "s/^#WaylandEnable=false/WaylandEnable=false/g" /etc/gdm3/custom.conf systemctl restart gdm3 dpkg-reconfigure gdm3 systemctl set-default graphical.target systemctl isolate multi-user.target systemctl isolate graphical.target tee /etc/X11/xorg.conf << EOF Section "Device" Identifier "DummyDevice" Driver "dummy" Option "UseEDID" "false" VideoRam 512000 EndSection Section "Monitor" Identifier "DummyMonitor" HorizSync 5.0 - 1000.0 VertRefresh 5.0 - 200.0 Option "ReducedBlanking" EndSection Section "Screen" Identifier "DummyScreen" Device "DummyDevice" Monitor "DummyMonitor" DefaultDepth 24 SubSection "Display" Viewport 0 0 Depth 24 Virtual 4096 2160 EndSubSection EndSection EOF systemctl isolate multi-user.target systemctl isolate graphical.target # VSCode apt install -y gpg apt-transport-https software-properties-common wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/ sh -c 'echo "deb [arch=arm64] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list' apt update -y apt install -y code # Amazon DCV wget https://d1uj6qtbmh3dt5.cloudfront.net/NICE-GPG-KEY gpg --import NICE-GPG-KEY wget https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-ubuntu2404-aarch64.tgz tar -xvzf nice-dcv-ubuntu2404-aarch64.tgz cd nice-dcv-2024.0-19030-ubuntu2404-aarch64 apt install -y ./nice-dcv-server_2024.0.19030-1_arm64.ubuntu2404.deb ./nice-dcv-web-viewer_2024.0.19030-1_arm64.ubuntu2404.deb ./nice-xdcv_2024.0.654-1_arm64.ubuntu2404.deb usermod -aG video dcv systemctl enable dcvserver cd /root rm nice-dcv-ubuntu2404-aarch64.tgz rm -fr nice-dcv-2024.0-19030-ubuntu2404-aarch64 sed -i "/^\[session-management\/automatic-console-session/a owner=\"ubuntu\"\nstorage-root=\"%home%\"" /etc/dcv/dcv.conf sed -i "s/^#create-session/create-session/g" /etc/dcv/dcv.conf # Create SSL cert apt install -y software-properties-common certbot python3-certbot-dns-route53 certbot certonly --dns-route53 -d vscode-${SystemName}-${SubName}.${DomainName} --agree-tos -m ${YourEmail} --non-interactive --preferred-challenges dns-01 cp /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/fullchain.pem /etc/dcv/dcv.pem cp /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/privkey.pem /etc/dcv/dcv.key chown dcv:dcv /etc/dcv/dcv.pem /etc/dcv/dcv.key chmod 600 /etc/dcv/dcv.pem /etc/dcv/dcv.key # Install nginx apt install -y nginx systemctl enable --now nginx mkdir -p /etc/nginx/ssl chmod 700 /etc/nginx/ssl cp /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/fullchain.pem /etc/nginx/ssl/dcv-chain.crt cp /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/privkey.pem /etc/nginx/ssl/dcv.key chmod 600 /etc/nginx/ssl/*.key chmod 644 /etc/nginx/ssl/*.crt tee /etc/nginx/conf.d/dcv.conf << EOF server { listen 443 ssl; server_name vscode-${SystemName}-${SubName}.${DomainName}; ssl_certificate_key /etc/nginx/ssl/dcv.key; ssl_certificate /etc/nginx/ssl/dcv-chain.crt; location / { proxy_pass https://127.0.0.1:8443; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_ssl_verify on; proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; proxy_ssl_verify_depth 2; proxy_ssl_name vscode-${SystemName}-${SubName}.${DomainName}; proxy_ssl_server_name on; } } EOF systemctl restart nginx # Create cert renew script cat << 'EOF' > /home/ubuntu/renew-cert.sh #!/bin/bash set -e sudo certbot renew --cert-name "vscode-${SystemName}-${SubName}.${DomainName}" sudo cp -f /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/fullchain.pem /etc/dcv/dcv.pem sudo cp -f /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/privkey.pem /etc/dcv/dcv.key sudo chown dcv:dcv /etc/dcv/dcv.pem /etc/dcv/dcv.key sudo chmod 600 /etc/dcv/dcv.pem /etc/dcv/dcv.key sudo cp -f /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/fullchain.pem /etc/nginx/ssl/dcv-chain.crt sudo cp -f /etc/letsencrypt/live/vscode-${SystemName}-${SubName}.${DomainName}/privkey.pem /etc/nginx/ssl/dcv.key sudo chmod 600 /etc/nginx/ssl/dcv.key sudo chmod 644 /etc/nginx/ssl/dcv-chain.crt sudo systemctl restart nginx echo "Certificate renewed." EOF chmod +x /home/ubuntu/renew-cert.sh chown ubuntu:ubuntu /home/ubuntu/renew-cert.sh # Install nvm sudo -u ubuntu bash -c "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash" # Download Amazon Q Developer CLI sudo -i -u ubuntu bash << EOF curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-aarch64-linux.zip" -o "q.zip" unzip q.zip EOF # Finally, reboot reboot Tags: - Key: Cost Value: !Sub ${SystemName}-${SubName} - Key: Name Value: !Sub vscode-${SystemName}-${SubName} DependsOn: - Ec2InstanceProfile - Route53RecordA - S3BucketMyDocuments # ------------------------------------------------------------# # Route 53 # ------------------------------------------------------------# Route53RecordA: Type: AWS::Route53::RecordSet Properties: HostedZoneName: !Sub ${DomainName}. Name: !Sub vscode-${SystemName}-${SubName}.${DomainName}. Type: A TTL: 300 ResourceRecords: - !GetAtt EipEc2.PublicIp DependsOn: EipEc2 # ------------------------------------------------------------# # Output Parameters # ------------------------------------------------------------# Outputs: # EIP DcvServerUrl8443: Value: !Sub "https://vscode-${SystemName}-${SubName}.${DomainName}:8443" DcvServerUrl: Value: !Sub "https://vscode-${SystemName}-${SubName}.${DomainName}"   リソース作成後の動作 出来上がりはこんな感じです。 AWS CloudFormation テンプレートの出力に、ブラウザアクセス用の URL があります。ubuntu ユーザーでログインします。ユーザーデータの実行に時間がかかるので、ブラウザアクセスはスタックの完了後 10 分程度待った方がいいです。気になる方は /var/log/cloud-init-output.log で状況を確認してください。 再度 ubuntu のログイン画面が表示されるので、ログインします。 VSCode も起動できました。クリップボードの共有もできます。 passwd コマンドで忘れずにパスワード変更しておきましょう。 ubuntu ユーザーのホームディレクトリに、Let’s Encrypt の証明書更新用スクリプト renew-cert.sh を作ってあります。これを実行すると証明書を更新してくれますが、有効期限が 30 日を切らないと更新できないようです。オプションで強制更新する設定もありましたが、そこまでするのはやめました。   まとめ いかがでしたでしょうか? 要件によってカスタマイズすることで、さらにいろいろなバリエーションを作れそうです。今後この環境を使って Kiro を試したいと思っています。 AWS Cloud9 は軽い点が良かったのですが、VSCode は重いですね・・・。インスタンスタイプが large 以上じゃないときついです。 本記事が皆様のお役に立てれば幸いです。
アバター
こんにちは、SCSKの中山です。 Cato SASEを導入・運用していく中で、お客様からよくご質問をいただく機能があります。 それは、 Bypass や Off-Cloud といった、一見すると「Catoを経由させないの?」と思えるような機能群です。また、同じ「Bypass」という名前が別の設定で出てきたり、機能が似ていて混同しやすかったりすることもあるかと思います。 そこで今回は、これらの「Catoを迂回する」ように見える機能について、それぞれの役割と違い、適切な使い分けを分かりやすく整理してみたいと思います。 今回整理するのは、以下の4つの機能です。 Bypass(ローカルブレイクアウト) Off-Cloud Split Tunnel Bypass (TLS Inspection) これらの機能を正しく理解することで、より柔軟で最適なネットワーク設計が可能になります。それでは、一つずつ見ていきましょう。   機能の整理:まずは大きな枠組みで理解しよう 今回ご紹介する機能は、大きく2つのカテゴリに分けることができます。 通信「経路」を迂回させる機能 :通信トラフィックそのものをCatoのクラウド(Cato PoP)を経由させないようにするものです。 Bypass(拠点からの通信) Off-Cloud(拠点間の通信) Split Tunnel(リモートアクセス端末からの通信) 特定の「セキュリティ機能」を迂回させる機能 :通信経路はCatoを経由させつつ、特定のセキュリティ処理だけをスキップさせるものです。 Bypass (TLS Inspection) この分類を頭に入れておくと、各機能の役割がイメージしやすくなります。それでは、まずは通信「経路」を迂回させる機能から解説します。   1. Bypass( 拠点 ):拠点からインターネットへの直接通信 こちらは、 拠点(Site)から特定のインターネット通信を、Cato PoPを経由させずに直接アクセスさせる ための機能です。一般的に「ローカルブレイクアウト」と呼ばれるものです。 【通常の通信】 通常、Catoを導入した拠点からのインターネット通信は、すべて最寄りのCato PoPを経由し、そこでセキュリティチェックを受けた上でインターネットに出ていきます。 【Bypass機能を使った通信】 Bypass機能を使うと、指定した宛先への通信だけ、Cato PoPを経由せず、拠点のCato Socketから直接インターネットへアクセスさせることができます。 ■ なぜこの機能が必要?(ユースケース) 特定のSaaSアプリケーションの通信最適化 :Microsoft 365やZoomなど、信頼性が高く、大量の通信を発生させるSaaSへのアクセスを直接行わせることで、Cato PoPでの処理遅延を回避し、パフォーマンスを向上させたい場合に必要です。 (契約されている帯域にもよりますが、Catoバックボーン経由で接続した方が早い場合があるので、検証が必要です。) Catoとの互換性問題の回避 :非常に稀ですが、特定のアプリケーションがCatoを経由すると正常に動作しない場合の回避策としてご利用可能です。 ■ 注意点 Bypassを設定した通信は、Catoの持つ高度なセキュリティ機能(IPS/IDS、次世代アンチマルウェア、サンドボックスなど)が 一切適用されません 。そのため、通信先の安全性が十分に確認できている信頼済みの宛先にのみ、限定的に利用することが強く推奨されます。なんでもかんでもBypassの対象にするのは避けましょう。   2. Off-Cloud:拠点間のプライベートな直接通信 こちらは、 Catoを導入している拠点(Site)間の通信を、Cato PoPを経由せずに直接行わせる ための機能です。 【通常の拠点間通信】 通常、拠点Aから拠点Bへ通信する場合、それぞれの拠点からCato PoPへ接続し、Catoのグローバルバックボーンネットワークを経由して通信が行われます。   【Off-Cloud機能を使った通信】 Off-Cloud機能を使うと、各拠点に設置されたCato Socket同士が直接IPsecトンネルを構築し、Cato Cloudを経由しないプライベートな通信路を確立します。 ■ なぜこの機能が必要?(ユースケース) 大容量の拠点間データ転送 :本社とデータセンター間での夜間バックアップなど、非常に大きなデータをやり取りする際に、Cato Cloudの帯域を消費せずに行いたい場合に必要な機能になります。 ■ 注意点 Bypass機能と同様に、Off-Cloudで設定された通信もCatoのセキュリティ機能は適用されません。拠点間の信頼できる通信に限定して利用する機能となります。   3. Split Tunnel( リモートアクセス) :リモートアクセスユーザーのためのローカルブレイクアウト こちらは、Cato Client(SDPユーザー)を利用している リモートアクセス版のローカルブレイクアウト 機能です。 【通常のCato Client接続】 ノートPCなどでCato Clientを有効にすると、デフォルトではPCからのすべての通信(インターネット向け、社内向け)がCato Cloudに送られます。これにより、社外にいても社内にいるのと同じセキュリティポリシーが適用されるわけですが、一つ不便な点があります。それは、自宅のプリンターやNASといったローカルネットワーク上の機器にアクセスできなくなることです。 ※プリンターへの通信もCato側へ行こうと仕様としてしまいます。   【Split Tunnel機能を使った通信】 Split Tunnelを設定すると、特定の通信をCatoのトンネルから除外(スプリット)できます。これにより、社内リソースへのアクセスはCato経由、自宅プリンターへのアクセスや特定のインターネット通信は自宅の回線から直接、といった使い分けが可能になります。 ■ なぜこの機能が必要?(ユースケース) ローカルデバイスへのアクセス :在宅勤務やオフィス内でClient接続していて、プリンターで印刷したり、NASにアクセスしたりする場合に必要になります。  4. Backhauling:特定の拠点からインターネットへ出る「折り返し」通信 こちらは、 一度Cato Cloudを経由した通信を、Cato PoPからではなく、指定した特定の拠点(サイト)からインターネットへアクセスさせる ための機能です。「バックホール(引き戻す)」という名前の通り、通信を特定の拠点に引き戻してから外に出す、というイメージです。 【通常のインターネット通信】 これまでと同様に、通常は拠点やリモートユーザーからのインターネット通信は、最寄りのCato PoPを経由して、そのままインターネットに出ていきます。   【Backhauling機能を使った通信】 Backhauling機能を使うと、対象の通信は一度Cato Cloudでセキュリティチェックを受けた後、PoPから直接インターネットに出るのではなく、指定された本社などの拠点へ転送されます。そして、その拠点のインターネット回線を使ってインターネットへアクセスします。 ■ なぜこの機能が必要?(ユースケース) 送信元IPアドレスを固定したい場合 :取引先のWebサイトや特定のSaaSが、アクセス元のグローバルIPアドレスで接続制限をかけているケースがあります。Cato PoPのIPアドレスは複数あるため、このような場合にBackhaulingを使い、特定の拠点が持つ固定のIPアドレスからアクセスさせることができます。 既存セキュリティ機器との段階的な移行 :Catoへの移行期間中に、既存のFirewallが持つ詳細なポリシーを一部の通信にだけ適用し続けたい、といった場合に、通信を一度既存Firewallに戻して処理させることができます。 ■ 他の機能との違い(特にBypassとの違い) Backhaulingは、Bypass(ローカルブレイクアウト)と混同されやすいですが、決定的な違いがあります。 Bypass :Cato Cloudを 全く経由しない 。そのため、Catoのセキュリティ機能は 適用されません 。 Backhauling :一度Cato Cloudを 必ず経由します 。そのため、Catoのセキュリティ機能(IPS/IDS、次世代アンチマルウェアなど)が 適用された上で 、指定の拠点からインターネットに出ていきます。 安全を確保しつつ、出口だけをコントロールしたい場合に非常に有効な機能です。 より詳しい設定方法や2つのモード(Internet Breakout / Local gateway IP)の違いについては、別の記事で詳しく解説します。 5. Bypass (TLS Inspection):特定のセキュリティ”処理”をスキップ 最後に登場するのが、これまでとは少し毛色の違う「Bypass」です。これはTLS Inspectionポリシーの中に出てくる用語で、 通信経路ではなく、特定のセキュリティ”処理”を対象外にする ための設定です。 ■ TLS Inspectionとは? まず、TLS Inspection(SSLインスペクションとも呼ばれます)は、HTTPSなどで暗号化された通信の中身をCatoが一旦復号し、脅威が含まれていないかをチェックする非常に重要なセキュリティ機能です。 ■ Bypass (TLS Inspection)の役割 TLS Inspectionポリシーのアクションには「Inspect(検査する)」と「Bypass(検査しない)」の2つがあります。ここで「Bypass」を選択すると、 その宛先との通信はCato PoPを経由するものの、TLS Inspectionの処理だけをスキップ します。つまり、暗号化された通信の中身をチェックせずに、そのまま通過させます。 ■ なぜこの機能が必要?(ユースケース) アプリケーションや通信先の互換性維持 :「証明書ピニング」という技術を使っている一部のアプリケーションやWebサイトは、TLS Inspectionを行うと中間者攻撃と誤認して通信が失敗することがあります。このような場合に、その宛先をBypass設定することで正常な通信を確保します。(金融機関のサイトなどが該当する場合が多いです。) ■ 他のBypassとの決定的な違い 「通信経路そのものをCatoから外す」BypassやSplit Tunnelとは異なり、こちらは「Catoの経路上で、特定の処理だけを行わない」という点が大きな違いです。混同しやすいポイントなので、しっかり区別して覚えておきましょう。   まとめ:各機能の違いが一目でわかる比較表 最後に、今回ご紹介した4つの機能の違いを表にまとめました。 機能名 対象スコープ 通信の向き先 Cato Cloudの経由 セキュリティ機能 主な目的 Bypass 拠点(Site) インターネット しない 適用外 特定SaaSへの通信最適化 Off-Cloud 拠点(Site) 他の拠点(Site) しない 適用外 大容量・低遅延の拠点間通信 Split Tunnel リモートユーザー(Client) ローカル しない 適用外 ローカルデバイスの利用 Bypass(TLS) 拠点/リモートユーザー すべて する TLS検査のみ適用外 通信先との互換性 このように整理すると、それぞれの役割と違いが明確になったのではないでしょうか。 Cato SASEの基本は、すべてのトラフィックをCato Cloudに集約し、一貫したセキュリティポリシーを適用することです。今回ご紹介した機能は、パフォーマンス上の要件や互換性の問題など、特別な理由がある場合に利用する「例外設定」と位置づけるのが良いでしょう。 これらの機能を適切に使いこなし、より安全で快適なネットワーク環境を構築していきましょう。
アバター
皆さんこんにちは! 学校の夏休みの宿題より塾の課題のほうが多かった小学生時代でした。 どうもいとさんです。 先日アマゾン ウェブサービス ジャパン合同会社主催の 『はじめてのAI駆動開発 on AWS』 に参加した際に、KiroとAmazon Q CLIを使用し簡単なゲームを作成したのでレポートとともに完成までの時間や特徴を比較していこうと思います! また本記事の作成にあたりNTT東日本株式会社の白鳥さんにご協力いただきました。 お忙しい所ご協力いただき誠にありがとうございました。 イベントレポート 今回はPartner限定ということで詳細はお話しできませんがこのような形でイベントが進んでいきました。 Amazon Q CLIについて・ユースケース Kiroの詳細・ユースケース ハンズオン:Amazon Q CLIでS3へのアップロードまで全部行おう(手作業での修正禁止✖) 成果物の発表 新しいKiroについては詳細を知らなかったのでとても有益な情報でした。 ユースケースに関してはAWSで環境構築している人向けの内容だと思いました。(トラブルシューティングなど) レベルが100~200向けということもあり初学者の私でも理解しやすい内容でした。   Amazon Q CLIとKiroの比較 今回ハンズオンではAmazon Q CLIの使用が主な目的でしたが、せっかくなのでKiroとの違いを見てみたいと思い 今回の機会を利用しプロンプトを投げてから完成までを比較してみました。 使用したプロンプト 差が出ないように以下の共通のプロンプトを使用いたしました。 こちらもKiroに作成してもらいました。 AWSサービスアイコンを使用したマッチ3風パズルゲームを開発します。プレイヤーは縦12マス×横6マスのフィールドでAWSサービスアイコンをクリックし、4つ以上縦横につながったアイコンを消してスコアを競います。難易度に応じてアイコンの種類が増え、1分間の制限時間内でできるだけ多くのアイコンを消すことが目標です。 Requirements Requirement 1 User Story: ゲームプレイヤーとして、AWSアイコンを使ったパズルゲームをプレイしたいので、直感的で楽しいゲーム体験を得たい Acceptance Criteria WHEN ゲームが開始されるとき THEN システムは縦12マス×横6マスのゲームフィールドを表示する WHEN ゲームが開始されるとき THEN システムは選択された難易度に応じたAWSアイコンをランダムに配置する WHEN プレイヤーがアイコンをクリックするとき THEN システムは同じ種類のアイコンが4つ以上縦横につながっているかを判定する WHEN 4つ以上のアイコンがつながっているとき THEN システムはそれらのアイコンを消去し、スコアに加算する WHEN アイコンが消去されるとき THEN システムは上から新しいアイコンを降らせて空いたスペースを埋める Requirement 2 User Story: ゲームプレイヤーとして、自分のスキルレベルに合った難易度でプレイしたいので、適切な挑戦レベルを選択できるようにしたい Acceptance Criteria WHEN ゲーム開始前に難易度を選択するとき THEN システムは「簡単」「普通」「難しい」「神」の4つの選択肢を提供する WHEN 「簡単」が選択されるとき THEN システムはEC2、VPC、S3、RDSの4種類のアイコンを使用する WHEN 「普通」が選択されるとき THEN システムは簡単レベルに加えてLambda、DynamoDB、API Gateway、EventBridgeを含む8種類のアイコンを使用する WHEN 「難しい」が選択されるとき THEN システムは普通レベルに加えてBedrock、SageMaker、Polly、Transcribeを含む12種類のアイコンを使用する WHEN 「神」が選択されるとき THEN システムは100種類のAWSサービスアイコンを使用する Requirement 3 User Story: ゲームプレイヤーとして、制限時間内でのスコアを競いたいので、明確な時間制限とスコア表示が欲しい Acceptance Criteria WHEN ゲームが開始されるとき THEN システムは1分間のカウントダウンタイマーを開始する WHEN タイマーが動作中のとき THEN システムは残り時間を秒単位で表示する WHEN アイコンが消去されるとき THEN システムは消去されたアイコン数をスコアに加算し、リアルタイムで表示する WHEN 制限時間が終了するとき THEN システムはゲームを終了し、最終スコアを表示する WHEN ゲーム終了時 THEN システムは新しいゲームを開始するオプションを提供する Requirement 4 User Story: ゲームプレイヤーとして、本物のAWSアイコンを使った視覚的に魅力的なゲームをプレイしたいので、公式のAWSアーキテクチャアイコンが使用されるべきだ Acceptance Criteria WHEN ゲームでアイコンが表示されるとき THEN システムはAWS公式アーキテクチャアイコン(https://aws.amazon.com/jp/architecture/icons/)を使用する WHEN アイコンが表示されるとき THEN システムは各アイコンを明確に識別できるサイズと品質で表示する WHEN アイコンが選択されるとき THEN システムは視覚的なフィードバック(ハイライトなど)を提供する WHEN アイコンが消去されるとき THEN システムはスムーズなアニメーション効果を表示する Requirement 5 User Story: ゲーム管理者として、シンプルなデプロイメントと配布を実現したいので、単一のHTMLファイルとしてゲームが動作する必要がある Acceptance Criteria WHEN ゲームが開発されるとき THEN システムは単一のHTMLファイルとして実装される WHEN HTMLファイルが作成されるとき THEN システムは必要なJavaScriptとCSSをインライン化する WHEN ゲームがデプロイされるとき THEN システムはS3バケットの静的Webサイトホスティングで動作する WHEN ゲームがロードされるとき THEN システムは外部依存関係なしに完全に動作する WHEN ブラウザでHTMLファイルが開かれるとき THEN システムは即座にゲームを開始できる状態になる 完成目標は以下5点です レベル選択ボタン、開始ボタンがあること 3つ以上でブロックが消えること スコアが記入されること 単一のHTMLファイルで構成されていること ブロックは公式アイコンを使用すること Amazon Q CLI まずはAmazon Q CLIに作ってもらいました。 Ver1 何も表示されないしゲームが始まりもしません… 修正しましょう 修正点 ブロックが表示されないので原因を調査してください(2回投げました) 公式アイコンのリンクを認識しなかったのでローカルからコピーしてください スタート画面がありません。作成してください ゲーム中に難易度が移動できないようにスタート画面に選択ボタンを移動してください。 ブロックが消える際に消したサービスの文字列を表示してください。消え方はフェードアウトで 完成版 全難易度のプレイ画面(特に何も変わらない…) カーソルをブロックに持っていくとサービス名が出ました(うれしい) 完成までは約37分 作成されたファイルはHTMLファイルのみで、使用したファイルは引用するためのアイコン画像ファイルです。 ・aws-puzzle-game.html ・─ icons/     ├── ec2.png     ├── s3.png     ├── rds.png     ├── vpc.png     └── lambda.png   Kiro 次はKiroです。 要件定義書➡詳細設計タスク作成約5分 このような感じでタスクの実行が進んでいきます。(チームメンバーの方のKiroで作成したため以下画面は別作成の物です) プロンプトからver1完成まで約1時間半かかり、task数は14になりました。 ver1はアイコンがサイトからの指定だと反映されなかったため絵文字になってしましました… ということで以下の点を修正いたしました。 修正点 アイコンが公式HPのファイルから反映できなかったのでローカルから読み込み、ブロックの配置を難易度ごとに変更 ブロックの消える際のエフェクト追加 カーソルの判定がおかしいので修正 完成版 簡単レベル 普通レベル 「難しい」レベル 「HERO」レベル Kiro版は完成まで約4時間半かかり 作成したファイルはHTMLファイルのみです。 ・aws-Icon-puzzle.html 使用したファイルは引用するためのアイコン画像ファイルです。 ・公式アイコンファイル(ブロックアイコンのため使用) 結果 完成目標はAmazon Q CLIはHEROレベルを考慮していないので作成時間に大きく時間に差が出ており、ver1までの時間はKiroのほうがかかりました。 Kiroの時間がかかった要因はファイルの検索、読み込み、デバッグや結合、単体テストを行っていた時間を含んでいたためであり、途中でゲームの動作確認は行えませんでした。 Kiroは時間はかかりましたがver1で表示されない、動かないなどのバグはなく完成版までの修正は少なかったです。 Amazon Q CLIは大枠のUIやシステムの作成は早いのですがデバッグなどは行わずコーディングしていくので、表示されない、選択できないなどバグが多く出やすかったです。 htmlファイル1つで作成しアクションを追加していたためKiro版は10000行を超えてしまいました。 これだけ大きくなってしまうと修正が難しくなっていったようです。(白鳥さん談)   特徴・まとめ Amazon Q CLIでのコーディングは複雑な指示を投げるより大雑把な指示を投げてから細かくプロンプトで指示を行うと手戻りが少なくなるのかなと思いました。 使用しているAWS環境での問題解決やWEBデザイン作成が向いていると感じました。(複雑なものは厳しいのかな) Kiroは時間がかかるが手戻りが少なく、大きなバグが少ない印象です。 AI仕様駆動開発の名の通り仕様書からテストまで一貫して行い、修正少なく楽に作成できました。 どちらも使用用途によってメリット・デメリットがありますが共通してコーディング・トラブルシュートが楽だなと感じました。 コンソールでも使用できるとのことだったのでコーディング以外でも使いこなしていきたいと思います! 本記事の作成にあたり、ご協力いただいたNTT東日本株式会社の白鳥さんに心より感謝申し上げます。 白鳥さんのサポートがあり、この記事を完成させることができました。ありがとうございます! おまけ 今回作成したAmazon Q CLIのゲームのみですが以下に記載させていただきます。 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AWS パズルゲーム</title> <style> body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: linear-gradient(135deg, #232F3E, #FF9900); color: white; text-align: center; } .game-container { max-width: 800px; margin: 0 auto; } .header { margin-bottom: 20px; } .stats { display: flex; justify-content: space-around; margin-bottom: 20px; font-size: 18px; font-weight: bold; } .game-board { display: grid; grid-template-columns: repeat(6, 50px); grid-template-rows: repeat(12, 50px); gap: 2px; justify-content: center; background: rgba(0,0,0,0.3); padding: 10px; border-radius: 10px; margin: 0 auto; } .cell { width: 50px; height: 50px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); border-radius: 5px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; position: relative; } .cell:hover { background: rgba(255,255,255,0.2); transform: scale(1.05); } .cell.selected { background: rgba(255,153,0,0.5); border-color: #FF9900; } .cell.matched { animation: fadeOut 1s ease-in-out forwards; } @keyframes fadeOut { 0% { opacity: 1; transform: scale(1); } 50% { opacity: 0.5; transform: scale(1.1); } 100% { opacity: 0; transform: scale(0.8); } } .service-name { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.8); color: white; padding: 4px 8px; border-radius: 4px; font-size: 10px; white-space: nowrap; opacity: 0; animation: fadeInOut 1s ease-in-out; z-index: 10; } @keyframes fadeInOut { 0% { opacity: 0; } 50% { opacity: 1; } 100% { opacity: 0; } } .icon { width: 40px; height: 40px; background-size: contain; background-repeat: no-repeat; background-position: center; } .start-btn { background: #FF9900; color: white; border: none; padding: 15px 30px; margin: 20px; border-radius: 5px; cursor: pointer; font-size: 18px; font-weight: bold; } .start-btn:hover { background: #e68900; } .start-btn:disabled { background: #666; cursor: not-allowed; } .start-screen { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 400px; } .game-screen { display: none; } .game-screen.active { display: block; } .difficulty-selector { margin-bottom: 20px; } .difficulty-btn { background: #FF9900; color: white; border: none; padding: 10px 20px; margin: 5px; border-radius: 5px; cursor: pointer; font-size: 16px; } .difficulty-btn:hover { background: #e68900; } .difficulty-btn.selected { background: #232F3E; } .game-over { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); display: none; align-items: center; justify-content: center; z-index: 1000; } .game-over-content { background: #232F3E; padding: 40px; border-radius: 10px; text-align: center; } .restart-btn { background: #FF9900; color: white; border: none; padding: 15px 30px; margin-top: 20px; border-radius: 5px; cursor: pointer; font-size: 18px; } </style> </head> <body> <div class="game-container"> <h1>AWS パズルゲーム</h1> <div class="start-screen" id="startScreen"> <h2>難易度を選択してください</h2> <div class="difficulty-selector"> <button class="difficulty-btn" data-level="easy">簡単 (4種類)</button> <button class="difficulty-btn" data-level="normal">普通 (8種類)</button> <button class="difficulty-btn" data-level="hard">難しい (12種類)</button> <button class="difficulty-btn" data-level="god">神 (100種類)</button> </div> <button class="start-btn" id="startBtn" onclick="startNewGame()">ゲーム開始</button> </div> <div class="game-screen" id="gameScreen"> <div class="stats"> <div>難易度: <span id="currentDifficulty">簡単</span></div> <div>スコア: <span id="score">0</span></div> <div>残り時間: <span id="timer">60</span>秒</div> </div> <div class="game-board" id="gameBoard"></div> <button class="restart-btn" onclick="backToStart()">スタート画面に戻る</button> </div> </div> <div class="game-over" id="gameOver"> <div class="game-over-content"> <h2>ゲーム終了!</h2> <p>最終スコア: <span id="finalScore">0</span></p> <button class="restart-btn" onclick="startNewGame()">新しいゲーム</button> </div> </div> <script> class AWSPuzzleGame { constructor() { this.board = []; this.score = 0; this.timeLeft = 60; this.gameActive = false; this.selectedCell = null; this.difficulty = 'easy'; this.timer = null; // AWS サービスアイコンのデータ this.awsIcons = { 'EC2': './icons/ec2.png', 'VPC': './icons/vpc.png', 'S3': './icons/s3.png', 'RDS': './icons/rds.png', 'Lambda': './icons/lambda.png', 'DynamoDB': './icons/ec2.png', 'API Gateway': './icons/s3.png', 'EventBridge': './icons/vpc.png', 'Bedrock': './icons/rds.png', 'SageMaker': './icons/lambda.png', 'Polly': './icons/ec2.png', 'Transcribe': './icons/s3.png' }; console.log('AWS Icons loaded:', this.awsIcons); // 神レベル用の追加アイコン(100種類まで拡張) this.godIcons = {}; for (let i = 1; i <= 100; i++) { this.godIcons[`Service${i}`] = this.getRandomColor(); } this.difficultySettings = { easy: Object.keys(this.awsIcons).slice(0, 4), normal: Object.keys(this.awsIcons).slice(0, 8), hard: Object.keys(this.awsIcons), god: Object.keys(this.godIcons) }; this.initializeGame(); } getRandomColor() { const colors = ['#FF9900', '#232F3E', '#569A31', '#3F48CC', '#FF4F8B', '#8C4FFF', '#FF6B6B', '#4ECDC4']; return colors[Math.floor(Math.random() * colors.length)]; } initializeGame() { this.createBoard(); this.setupEventListeners(); } createBoard() { const gameBoard = document.getElementById('gameBoard'); gameBoard.innerHTML = ''; this.board = []; for (let row = 0; row < 12; row++) { this.board[row] = []; for (let col = 0; col < 6; col++) { const cell = document.createElement('div'); cell.className = 'cell'; cell.dataset.row = row; cell.dataset.col = col; const icon = document.createElement('div'); icon.className = 'icon'; cell.appendChild(icon); gameBoard.appendChild(cell); this.board[row][col] = this.getRandomIcon(); this.updateCellDisplay(cell, this.board[row][col]); } } } getRandomIcon() { const icons = this.difficultySettings[this.difficulty]; return icons[Math.floor(Math.random() * icons.length)]; } updateCellDisplay(cell, iconType) { const icon = cell.querySelector('.icon'); if (this.difficulty === 'god') { const color = this.godIcons[iconType]; icon.style.background = color; icon.style.backgroundImage = 'none'; icon.style.borderRadius = '50%'; } else { const imagePath = this.awsIcons[iconType]; icon.style.backgroundImage = `url('${imagePath}')`; icon.style.backgroundColor = '#FF9900'; icon.style.borderRadius = '5px'; } icon.title = iconType; console.log('Icon updated:', iconType, this.awsIcons[iconType]); } setupEventListeners() { document.querySelectorAll('.difficulty-btn').forEach(btn => { btn.addEventListener('click', (e) => { this.setDifficulty(e.target.dataset.level); }); }); document.getElementById('gameBoard').addEventListener('click', (e) => { if (!this.gameActive) return; const cell = e.target.closest('.cell'); if (cell) { this.handleCellClick(cell); } }); } setDifficulty(level) { if (this.gameActive) return; // ゲーム中は難易度変更不可 this.difficulty = level; document.querySelectorAll('.difficulty-btn').forEach(btn => { btn.classList.remove('selected'); }); document.querySelector(`[data-level="${level}"]`).classList.add('selected'); // 難易度名を更新 const difficultyNames = { easy: '簡単', normal: '普通', hard: '難しい', god: '神' }; document.getElementById('currentDifficulty').textContent = difficultyNames[level]; } handleCellClick(cell) { const row = parseInt(cell.dataset.row); const col = parseInt(cell.dataset.col); const iconType = this.board[row][col]; if (this.selectedCell) { this.selectedCell.classList.remove('selected'); } this.selectedCell = cell; cell.classList.add('selected'); const matches = this.findMatches(row, col); if (matches.length >= 3) { this.removeMatches(matches, iconType); this.score += matches.length; document.getElementById('score').textContent = this.score; setTimeout(() => this.dropIcons(), 1000); } setTimeout(() => { if (this.selectedCell) { this.selectedCell.classList.remove('selected'); this.selectedCell = null; } }, 500); } findMatches(row, col) { const iconType = this.board[row][col]; const visited = new Set(); const matches = []; const dfs = (r, c) => { const key = `${r},${c}`; if (visited.has(key) || r < 0 || r >= 12 || c < 0 || c >= 6 || this.board[r][c] !== iconType) { return; } visited.add(key); matches.push({row: r, col: c}); // 縦横の隣接セルをチェック dfs(r-1, c); // 上 dfs(r+1, c); // 下 dfs(r, c-1); // 左 dfs(r, c+1); // 右 }; dfs(row, col); return matches; } removeMatches(matches, serviceName) { matches.forEach(match => { const cell = document.querySelector(`[data-row="${match.row}"][data-col="${match.col}"]`); cell.classList.add('matched'); // サービス名を表示 const nameDiv = document.createElement('div'); nameDiv.className = 'service-name'; nameDiv.textContent = serviceName; cell.appendChild(nameDiv); this.board[match.row][match.col] = null; }); setTimeout(() => { matches.forEach(match => { const cell = document.querySelector(`[data-row="${match.row}"][data-col="${match.col}"]`); cell.classList.remove('matched'); const nameDiv = cell.querySelector('.service-name'); if (nameDiv) nameDiv.remove(); }); }, 1000); } dropIcons() { for (let col = 0; col < 6; col++) { let writePos = 11; // 下から上へスキャンして、nullでないアイコンを下に詰める for (let row = 11; row >= 0; row--) { if (this.board[row][col] !== null) { this.board[writePos][col] = this.board[row][col]; if (writePos !== row) { this.board[row][col] = null; } writePos--; } } // 上の空いた部分に新しいアイコンを追加 for (let row = writePos; row >= 0; row--) { this.board[row][col] = this.getRandomIcon(); } } // 表示を更新 this.updateDisplay(); } updateDisplay() { for (let row = 0; row < 12; row++) { for (let col = 0; col < 6; col++) { const cell = document.querySelector(`[data-row="${row}"][data-col="${col}"]`); this.updateCellDisplay(cell, this.board[row][col]); } } } startGame() { // スタート画面を非表示、ゲーム画面を表示 document.getElementById('startScreen').style.display = 'none'; document.getElementById('gameScreen').classList.add('active'); this.gameActive = true; this.score = 0; this.timeLeft = 60; document.getElementById('score').textContent = this.score; document.getElementById('timer').textContent = this.timeLeft; this.timer = setInterval(() => { this.timeLeft--; document.getElementById('timer').textContent = this.timeLeft; if (this.timeLeft <= 0) { this.endGame(); } }, 1000); this.createBoard(); } endGame() { this.gameActive = false; clearInterval(this.timer); document.getElementById('finalScore').textContent = this.score; document.getElementById('gameOver').style.display = 'flex'; } } let game; function startNewGame() { document.getElementById('gameOver').style.display = 'none'; if (!game) { game = new AWSPuzzleGame(); } game.startGame(); } function backToStart() { if (game) { game.gameActive = false; if (game.timer) clearInterval(game.timer); } document.getElementById('gameScreen').classList.remove('active'); document.getElementById('startScreen').style.display = 'flex'; document.getElementById('gameOver').style.display = 'none'; } // ページ読み込み時にゲームを初期化 window.addEventListener('load', () => { game = new AWSPuzzleGame(); // デフォルトで簡単レベルを選択 document.querySelector('[data-level="easy"]').classList.add('selected'); game.setDifficulty('easy'); }); </script> </body> </html>
アバター
今回は、AWS Systems Managerのパッチ適用自動化に続いて、CloudTrailの監査ログ収集をAWS CDKで実装する方法をまとめました。 はじめに 今回は、CloudTrailログの収集からS3への保存、KMS暗号化による保護までをAWS CDKでコード化していきます。   今回作成するリソース S3バケット : CloudTrailログの保存先 KMSキー : ログファイルの暗号化用 CloudTrail : API呼び出しの記録と配信 IAMポリシー : S3バケットアクセス制御とKMS権限設定 ライフサイクルポリシー : 366日での自動削除設定   AWS CDK ソースコード S3バケットの設定 const cloudTrailBucket = new s3.Bucket(this, 'CloudTrailLogsBucket', { bucketName: `s3b-cloudtrail-logs-bucket01`, // バケット名 blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // パブリックアクセスをすべてブロック encryption: s3.BucketEncryption.S3_MANAGED, // 暗号化タイプ:SSE-S3 autoDeleteObjects: true, // バケット削除時にオブジェクトも削除 デプロイ成功後にfalseに設定 enforceSSL: true, // SSL/TLS暗号化を強制 versioned: true, // バージョニング有効化 lifecycleRules: [ // ライフサイクルルール作成 { id: 'CloudTrailLogsLifecycle', // ライフサイクルルール名 expiration: cdk.Duration.days(366), // 1年間保持 } ], removalPolicy: cdk.RemovalPolicy.DESTROY, // スタック削除時にバケットも削除 デプロイ成功後にRETAINに変更 }); ポイント: bucketName : 環境に応じてユニークな名前に変更が必要 blockPublicAccess : セキュリティのため完全ブロック encryption : S3管理暗号化を使用 versioned : ログ改ざん防止のためバージョニング有効 lifecycleRules : コンプライアンス要件に応じて保持期間を調整 本番環境では autoDeleteObjects: false ,  removalPolicy: RETAIN に変更を推奨 S3バケットポリシーの設定 // 初回デプロイ用: CloudTrailバケットポリシー(デプロイ後コメントアウト) // GetBucketAcl には条件を付けない cloudTrailBucket.addToResourcePolicy(new iam.PolicyStatement({ // バケットポリシー追加1 sid: 'AWSCloudTrailGetBucketAcl',        // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: [ // 許可するアクション 's3:GetBucketAcl', // バケットのACLを取得 ], resources: [cloudTrailBucket.bucketArn] // 対象バケット })); // 初回デプロイ用: CloudTrailバケットポリシー(デプロイ後コメントアウト) // PutObject のみ条件付き cloudTrailBucket.addToResourcePolicy(new iam.PolicyStatement({ // バケットポリシー追加2 sid: 'AWSCloudTrailPutObject', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: ['s3:PutObject'], // オブジェクトの配置を許可 resources: [`${cloudTrailBucket.bucketArn}/*`], // 対象バケット配下のすべてのオブジェクト conditions: { // 条件 StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' // バケット所有者にフルコントロールを付与 } } })); ポイント: CloudTrailサービスのみにアクセス権限を付与 s3:x-amz-acl 条件でバケット所有者権限を保証 ログファイルの改ざん防止対策 CloudTrail作成後バケットポリシーが重複するため、構築後バケットポリシーをコメントアウト KMSキーの設定 const cloudTrailKey = new kms.Key(this, 'CloudTrailKey', { description: 'KMS key for CloudTrail logs encryption', // キーの説明 enableKeyRotation: true, // キーローテーション有効化 removalPolicy: cdk.RemovalPolicy.DESTROY, // スタック削除時にキーも削除 デプロイ成功後にRETAINに変更 }); cdk.Tags.of(cloudTrailKey).add('Name', 'CloudTrailKey'); // タグ付け // KMSキーのエイリアスを作成 new kms.Alias(this, 'CloudTrailKeyAlias', { aliasName: 'alias/cloudtrail-audit-key', // エイリアス名 targetKey: cloudTrailKey, // 対象キー }); ポイント: enableKeyRotation: true : セキュリティ強化のため自動キーローテーション エイリアス設定により、キーの管理と識別が容易 本番環境では removalPolicy: RETAIN に変更を推奨 KMSキーポリシーの設定 // CloudTrailサービスがログを暗号化できる権限 cloudTrailKey.addToResourcePolicy(new iam.PolicyStatement({ // KMSキーポリシー追加1 sid: 'Allow CloudTrail to encrypt logs', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: ['kms:GenerateDataKey*'], // データキー生成を許可 resources: ['*'], // すべてのリソース conditions: { // 条件 StringLike: { 'kms:EncryptionContext:aws:cloudtrail:arn': `arn:aws:cloudtrail:*:${cdk.Aws.ACCOUNT_ID}:trail/*` } } })); // CloudTrailサービスがキーを記述できる権限 cloudTrailKey.addToResourcePolicy(new iam.PolicyStatement({ // KMSキーポリシー追加2 sid: 'Allow CloudTrail to describe key', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: ['kms:DescribeKey'], // キー情報の記述を許可 resources: ['*'] // すべてのリソース })); // アカウント内のプリンシパルがログファイルを復号化できる権限 cloudTrailKey.addToResourcePolicy(new iam.PolicyStatement({ // KMSキーポリシー追加3 sid: 'Allow principals in the account to decrypt log files', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.AnyPrincipal()], // すべてのプリンシパル actions: [ // 許可するアクション 'kms:Decrypt', // 復号化 'kms:ReEncryptFrom' // 再暗号化 ], resources: ['*'], // すべてのリソース conditions: { // 条件 StringEquals: { 'kms:CallerAccount': cdk.Aws.ACCOUNT_ID // 呼び出し元アカウント }, StringLike: { 'kms:EncryptionContext:aws:cloudtrail:arn': `arn:aws:cloudtrail:*:${cdk.Aws.ACCOUNT_ID}:trail/*` } } })); ポイント: CloudTrailサービス専用の暗号化権限設定 アカウント内限定の復号化権限 kms:EncryptionContext 条件でCloudTrail専用に制限 CloudTrailの設定 const trail = new cloudtrail.Trail(this, 'CloudTrail', { trailName: 'Audit', // CloudTrail名 bucket: cloudTrailBucket, // ログ出力先S3バケット includeGlobalServiceEvents: true, // グローバルサービスイベントを含める isMultiRegionTrail: true, // マルチリージョントレイルとして設定 enableFileValidation: true, // ファイル検証を有効化 sendToCloudWatchLogs: false, // CloudWatch Logsへの送信は無効 managementEvents: cloudtrail.ReadWriteType.ALL, // 管理イベント:すべて(読み取り・書き込み) encryptionKey: cloudTrailKey, // 暗号化キー }); ポイント: isMultiRegionTrail: true : 全リージョンのイベントを記録 includeGlobalServiceEvents: true : IAM、CloudFrontなどグローバルサービスも記録 enableFileValidation: true : ログファイルの完全性検証 managementEvents: ALL : 読み取り・書き込み両方の管理イベントを記録 データイベントは必要に応じて別途設定 管理イベントのみ記録   今回実装したコンストラクトファイルまとめ import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as cloudtrail from 'aws-cdk-lib/aws-cloudtrail'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as kms from 'aws-cdk-lib/aws-kms'; import * as iam from 'aws-cdk-lib/aws-iam'; export class CloudTrailStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id); //=========================================== // CloudTrail ログ用のS3バケット作成 //=========================================== const cloudTrailBucket = new s3.Bucket(this, 'CloudTrailLogsBucket', { bucketName: `s3b-cloudtrail-logs-bucket01`, // バケット名 blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // パブリックアクセスをすべてブロック encryption: s3.BucketEncryption.S3_MANAGED, // 暗号化タイプ:SSE-S3 autoDeleteObjects: true, // バケット削除時にオブジェクトも削除 デプロイ成功後にfalseに設定 enforceSSL: true, // SSL/TLS暗号化を強制 versioned: true, // バージョニング有効化 lifecycleRules: [ // ライフサイクルルール作成 { id: 'CloudTrailLogsLifecycle', // ライフサイクルルール名 expiration: cdk.Duration.days(366), // 1年間保持 } ], removalPolicy: cdk.RemovalPolicy.DESTROY, // スタック削除時にバケットも削除 デプロイ成功後にRETAINに変更 }); // 初回デプロイ用: CloudTrailバケットポリシー(デプロイ後コメントアウト) // GetBucketAcl には条件を付けない cloudTrailBucket.addToResourcePolicy(new iam.PolicyStatement({ // バケットポリシー追加1 sid: 'AWSCloudTrailGetBucketAcl',        // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: [ // 許可するアクション 's3:GetBucketAcl', // バケットのACLを取得 ], resources: [cloudTrailBucket.bucketArn] // 対象バケット })); // 初回デプロイ用: CloudTrailバケットポリシー(デプロイ後コメントアウト) // PutObject のみ条件付き cloudTrailBucket.addToResourcePolicy(new iam.PolicyStatement({ // バケットポリシー追加2 sid: 'AWSCloudTrailPutObject', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: ['s3:PutObject'], // オブジェクトの配置を許可 resources: [`${cloudTrailBucket.bucketArn}/*`], // 対象バケット配下のすべてのオブジェクト conditions: { // 条件 StringEquals: { 's3:x-amz-acl': 'bucket-owner-full-control' // バケット所有者にフルコントロールを付与 } } })); //=========================================== // CloudTrail用のKMSキーを作成 //=========================================== const cloudTrailKey = new kms.Key(this, 'CloudTrailKey', { description: 'KMS key for CloudTrail logs encryption', // キーの説明 enableKeyRotation: true, // キーローテーション有効化 removalPolicy: cdk.RemovalPolicy.DESTROY, // スタック削除時にキーも削除 デプロイ成功後にRETAINに変更 }); cdk.Tags.of(cloudTrailKey).add('Name', 'CloudTrailKey'); // タグ付け // KMSキーのエイリアスを作成 new kms.Alias(this, 'CloudTrailKeyAlias', { aliasName: 'alias/cloudtrail-audit-key', // エイリアス名 targetKey: cloudTrailKey, // 対象キー }); // CloudTrailサービスがログを暗号化できる権限 cloudTrailKey.addToResourcePolicy(new iam.PolicyStatement({ // KMSキーポリシー追加1 sid: 'Allow CloudTrail to encrypt logs', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: ['kms:GenerateDataKey*'], // データキー生成を許可 resources: ['*'], // すべてのリソース conditions: { // 条件 StringLike: { 'kms:EncryptionContext:aws:cloudtrail:arn': `arn:aws:cloudtrail:*:${cdk.Aws.ACCOUNT_ID}:trail/*` } } })); // CloudTrailサービスがキーを記述できる権限 cloudTrailKey.addToResourcePolicy(new iam.PolicyStatement({ // KMSキーポリシー追加2 sid: 'Allow CloudTrail to describe key', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.ServicePrincipal('cloudtrail.amazonaws.com')], // CloudTrailサービス actions: ['kms:DescribeKey'], // キー情報の記述を許可 resources: ['*'] // すべてのリソース })); // アカウント内のプリンシパルがログファイルを復号化できる権限 cloudTrailKey.addToResourcePolicy(new iam.PolicyStatement({ // KMSキーポリシー追加3 sid: 'Allow principals in the account to decrypt log files', // ステートメントID effect: iam.Effect.ALLOW, // 許可 principals: [new iam.AnyPrincipal()], // すべてのプリンシパル actions: [ // 許可するアクション 'kms:Decrypt', // 復号化 'kms:ReEncryptFrom' // 再暗号化 ], resources: ['*'], // すべてのリソース conditions: { // 条件 StringEquals: { 'kms:CallerAccount': cdk.Aws.ACCOUNT_ID // 呼び出し元アカウント }, StringLike: { 'kms:EncryptionContext:aws:cloudtrail:arn': `arn:aws:cloudtrail:*:${cdk.Aws.ACCOUNT_ID}:trail/*` } } })); //=========================================== // CloudTrailの作成 //=========================================== const trail = new cloudtrail.Trail(this, 'CloudTrail', { trailName: 'Audit', // CloudTrail名 bucket: cloudTrailBucket, // ログ出力先S3バケット includeGlobalServiceEvents: true, // グローバルサービスイベントを含める isMultiRegionTrail: true, // マルチリージョントレイルとして設定 enableFileValidation: true, // ファイル検証を有効化 sendToCloudWatchLogs: false, // CloudWatch Logsへの送信は無効 managementEvents: cloudtrail.ReadWriteType.ALL, // 管理イベント:すべて(読み取り・書き込み) encryptionKey: cloudTrailKey, // 暗号化キー }); } }   まとめ 今回は、AWS CloudTrailを使用した監査ログ収集システムをAWS CDKで実装しました。 IaCとして管理することで、環境間での一貫した設定の展開や、設定変更の履歴管理も可能になります。 皆さんのお役に立てば幸いです。
アバター
こんにちは、SCSKの坂木です。 本記事では、 Zabbixサーバのインストールを自動化するシェルスクリプト(RHEL系OS/PostgreSQL編)を紹介 します!   検証環境 今回、検証に使用した環境は以下の通りです。 コンポーネント バージョン/種類 備考 OS Amazon Linux 2023 RHEL系のディストリビューション データベース PostgreSQL Zabbixで広く利用されているデータベース Zabbix Zabbix7.0 LTS 2025/08時点での最新の長期サポート版 この構成に基づき、本ブログで公開するスクリプトは 「RHEL系OS + PostgreSQL」の環境に、Zabbix 7.0 LTSをインストール するものとなっています。 もし異なるOS(Ubuntuなど)やデータベース(MySQLなど)をご希望の場合は、一部カスタマイズが必要となりますので、あらかじめご了承ください。   前提条件 PostgreSQLサーバが起動していること 本スクリプトはZabbixサーバのコンポーネントをインストールし、DBに接続する設定を行いますが、PostgreSQLサーバ自体のインストールや起動は行いません。あらかじめMySQLが利用可能な状態にしておいてください。 OSがZabbixサーバのバージョンに対応していること Zabbixサーバはバージョンごとに公式にサポートされるOSが定められています。対応しているOSのバージョンは こちら を確認してください。 RHEL系のOSであること このスクリプトは、パッケージ管理にdnfコマンドを利用するなど、RHEL系のOSに特化して作成されています。   インストールスクリプト スクリプトはこちらになります。リポジトリのインストール元は こちら を参照しOSに合わせて変更ください。 #!/bin/bash set -e # 1. ZabbixがPostgreSQLデータベース接続用に使うパスワード ZABBIX_DB_PASS='<PostgreSQLのzabbixユーザのパスワードを入力してください>' echo "--- Zabbixリポジトリの追加とパッケージのインストールを開始します ---" rpm -Uvh https://repo.zabbix.com/zabbix/7.0/amazonlinux/2023/x86_64/zabbix-release-latest-7.0.amzn2023.noarch.rpm dnf clean all dnf install -y zabbix-server-pgsql zabbix-web-pgsql zabbix-apache-conf zabbix-sql-scripts zabbix-agent echo "--- Zabbix用データベースとユーザーを作成します ---" sudo -i -u postgres psql -c "CREATE USER zabbix WITH PASSWORD '${ZABBIX_DB_PASS}';" sudo -i -u postgres createdb -O zabbix zabbix echo "--- Zabbix初期スキーマとデータをインポートします ---" zcat /usr/share/zabbix-sql-scripts/postgresql/server.sql.gz | sudo -u zabbix psql zabbix echo "--- Zabbixサーバーの設定ファイル(/etc/zabbix/zabbix_server.conf)を編集します ---" sed -i "s/^# DBPassword=/DBPassword=${ZABBIX_DB_PASS}/" /etc/zabbix/zabbix_server.conf echo "--- 関連サービスを再起動し、自動起動を有効化します ---" systemctl restart zabbix-server zabbix-agent httpd php-fpm systemctl enable zabbix-server zabbix-agent httpd php-fpm echo "--- Zabbixのセットアップが正常に完了しました ---"   適当なディレクトリに配置してスクリプトを実行してください。実行前にはスクリプト内容をご確認いただき、ご自身の責任で実施いただければと思います。 [root@ip-10-0-30-49 work]# ll total 4 -rwxr-xr-x. 1 root root 1385 Sep 1 06:43 zabbixinstallscript.sh [root@ip-10-0-30-49 work]# ./zabbixautoinstall.sh   実行確認 スクリプト実行後、問題なくzabbixが起動しているか以下のポイントを確認します。 systemctl statusコマンドでzabbixサーバ、zabbixエージェントが起動していること # systemctl status zabbix-server ● zabbix-server.service - Zabbix Server Loaded: loaded (/usr/lib/systemd/system/zabbix-server.service; enabled; preset: disabled) Active: active (running) since Mon 2025-09-01 06:44:39 UTC; 18min ago # systemctl status zabbix-agent ● zabbix-agent.service - Zabbix Agent Loaded: loaded (/usr/lib/systemd/system/zabbix-agent.service; enabled; preset: disabled) Active: active (running) since Mon 2025-09-01 06:44:39 UTC; 18min ago   zabbixのwebインタフェースにアクセスできること   まとめ 本記事では、スクリプトを実行するだけでZabbix 7.0を自動インストールする手順をご紹介しました。なお、本記事で提供するスクリプトは、以下の環境を前提としています。 OS: RHEL系 (Rocky Linux, AlmaLinux など) データベース: PostgreSQL Zabbixバージョン: 7.0 もし上記以外の環境(例: Ubuntu, MySQL)でZabbixを構築されたい場合は、本スクリプトは対応しておりませんのでご注意ください。その場合は、Zabbix公式ドキュメントを参考に手動でインストールをお試しいただくか、OS/データベースに合うスクリプトをご利用ください。 OS:RHEL系 / データベース:PostgreSQLのスクリプトは以下記事に記載があります。 スクリプトを用いてZabbixサーバのインストールを自動化してみた-RHEL系OS/MySQL編- Zabbixサーバのインストールを自動化するシェルスクリプトを紹介します!初心者でも簡単に短時間でZabbixサーバの構築が可能となります。スクリプトを用いた構築で効率化をしましょう! blog.usize-tech.com 2025.09.02
アバター
こんにちは。SCSKの磯野です。 Google Cloud では、複数プロジェクトのログを集約・可視化する方法として「 ログスコープ 」と「 ログシンク 」の2つが提供されています。 では、組織全体のログ監視を設計する際、どちらを選択すべきなのでしょうか。 本記事では両者の仕組みとユースケースを比較し、最適な選択肢を導くためのポイントを解説します。 プロジェクトをまたいだログの閲覧 ログシンク(ログルータによる集約シンク)とは 集約シンクの概要  |  Cloud Logging  |  Google Cloud ログストレージを集中管理できる集約シンクについて学びます。 cloud.google.com 集約シンクを使うと 組織やフォルダ単位でログを一元的に集めること ができます。 集約シンクには2つのモードがあります。 インターセプトモード 集約対象のログを一か所に移動するため、元のプロジェクトからログが見えなくなる。権限管理には注意が必要 ログの重複保管を防ぎ、コスト削減になる 非インターセプトモード(デフォルト) 集約対象のログを元のプロジェクトと集約先の両方に保存 プロジェクト担当者・集約先担当者の両方がログを確認できる ログの重複保管が発生するため、コストがかかる なお、集約シンクを使わずに個別プロジェクトごとにログルータを設定することも可能です。 ログスコープとは ログの実体を移動することなく、複数プロジェクトのログを まとめて参照できる機能 です。 コストは安価ですが、あくまで「ビュー」のような存在であり、後述する監視用途では制約があります。   複数プロジェクトのログ監視ではどちらを採用すべき? 前提 複数のプロジェクトのログ監視を行うにあたり「 ログ監視を行うプロジェクトを一か所に集約できること 」を目指します。 結論:ログシンクを採用すべき 理由は以下の通りです。 ログベースのアラートはログスコープに対応していない 以下ドキュメントにもあるように、ログベースのアラートポリシーは「そのポリシーを設定したプロジェクト内のログ」にしか作用しません。 そのため、ログスコープを使って複数プロジェクトのログを参照しても、アラートポリシーは動作しません。 Log-based alerting policies don’t honor the scope setting in the Logs Explorer page. Configure log-based alerting policies  |  Cloud Logging  |  Google Cloud cloud.google.com ログシンクなら対応可能 ログシンクを使えば、集約先のプロジェクトにログをコピーできるため、そのプロジェクトを基点にアラートポリシーを作成できます。 Google Cloud における監視方法について- モニタリングとロギング Google Cloudにおける監視はログ監視とメトリクス監視の2種類に分けることができます。Cloud Loggingの使い方、Cloud Monitoringにおけるクエリの書き方、Terraformでアラート実装方法等をご紹介します。 blog.usize-tech.com 2025.06.09   ログスコープ/ログシンクの比較・ユースケース 項目 ログスコープ ログシンク 設定方法 △ プロジェクトレベルで指定可能 フォルダ配下のプロジェクトを動的に指定できないため、大規模プロジェクトの管理には不向き ○ フォルダレベルで指定可能 コスト ○ 参照のみなので安価 △ ログがコピーされるため転送先に応じてコスト発生 クォータ △ 1つのログスコープあたり最大5プロジェクトまで 参考: クォータ ○ 記載なし(プロジェクトや転送先に依存) 監視設定 × ログベースのアラートポリシーには利用できない ○ 可能(Cloud Monitoringのアラートなどに活用可能) 主なユースケース プロジェクト間のログをまとめて 横断的に調査・分析 したい場合 開発・検証などコストを抑えたい場合 アラート検知や監査対応 が必要な場合 ログを長期保存(BigQuery/Cloud Storage)したい場合 セキュリティや運用要件で 外部に転送(Pub/Sub → SIEMなど)したい場合   まとめ いかがだったでしょうか。 今回は、Google Cloud における ログスコープとログシンクの違い 、そして ログ監視を設計する際にどちらを使うべきか を整理しました。 結論として、 監視やアラート設定を前提とするなら「ログシンク」 を採用するのが実運用上は有効です。 一方で、コストを抑えて横断的に参照したいだけなら「ログスコープ」も選択肢になります。 本記事が、みなさまのログ設計の参考になれば幸いです。
アバター
こんにちは。SCSKの津田です。 今回は、Azureの環境で仮想IPアドレスをどのように機能させるかについてご紹介します。 クラウド環境では、オンプレミスで当たり前に使えていた「仮想IP(VIP)」が、実はそのままでは使えないケースが多くあります。 AWSでは、第4回でご紹介したように、EC2やRoute 53といったリソースを活用することで、クライアントからの接続を実現していました。 【LifeKeeper】AWSでは仮想IPアドレスが使えない!?をこうして解決する!! – TechHarmony では、Azureではどうでしょうか? Azureでは、AWSとは異なるアプローチで仮想IPを実現しています。 本記事では、実際に仮想IPを実装し、簡単な動作確認を行ってみました。 Azure環境では仮想IPが使えない?ではどうする? 仮想IPは、HAクラスタ構成において非常に重要な役割を果たします。 仮想IPは現在稼働しているノード(アプリケーションが実行されているノード)に常にマッピングされ、クライアントは常にこの仮想IPアドレスでアクセスできることで、障害時でもクライアントは意識することなくシステムを利用することができます。 しかし、Azure の仮想ネットワークでは LifeKeeper で設定した仮想IPがオンプレや仮想環境のようには認識されません。 そのため、通常では仮想IPを使用したネットワーク通信や保護はできません。 この問題を解決するため、 Azureではロードバランサー、LifeKeeperのLB Health Check Kitを使用することで仮想IPを機能させます。 クラスタを同一リージョン内に構築する場合、 内部ロードバランサー(Internal Load Balancer、ILB)でフロントエンドIPを設定し、バックエンドプールに稼働系ノードと待機系ノードのNICの IP アドレスに設定し、そのIPアドレス宛に正常性プロープを行います。 ILBのフロントエンドIPは仮想IPとして活用でき、クライアントからの仮想IP宛のネットワーク通信はILBに到達し、クラスタに負荷分散されます。負荷分散は正常性プローブで応答が返り正常な結果となるノードにのみ行われます。 この正常性プローブに対しノード側で応答するのがLB Health Check Kit (LB Health Checkリソース) の機能であり、 LifeKeeperによってLB Health Check Kitはアクティブ側のみで稼働し、正常性プローブに応答する状態となっています。 よって、 クライアントからのネットワーク通信はILB経由で常にアクティブなノードに転送され、LifeKeeperによってアクティブノードに付与された仮想IPで接続要求を受信し応答を返します。 SIOSオンラインマニュアルには、仮想IPアドレスを利用したクライアントからの接続フローがイメージ図付きで解説されています! ■Windows Azure 特有の設定について – LifeKeeper for Windows LIVE – 8.11.0 ■Linux Azure 上の LifeKeeper 特有の設定について – LifeKeeper for Linux LIVE – 9.9.1 正常性プローブとは? 「プローブ(probe)」とは、「探査」を意味する用語であり、Azure 正常性プローブとは、バックエンドプールの正常性を監視するためのヘルスチェックのような機能です。 正常性プローブへの応答がない仮想マシンについては適切に検出し、新しい接続の送信を停止します。 LB Health Check Kitとは? クラウド環境でロードバランサーの負荷分散対象インスタンスへのヘルスチェックプローブを待ち受けて応答する仕組みを提供します。 参考: https://docs.us.sios.com/sps/8.11.0/ja/topic/lifekeeper-for-linux-lbhc   検証した構成 今回は実際に仮想IPを実装してみました。 以下はノード・ネットワーク構成のイメージ図となります。 ※「LBHC」= LB Health Checkの略 ■OS:Windows Server 2022 Datacenter ■LifeKeeper:LifeKeeper for Windows 8.11.0 ※Windowsでの注意点として、LifeKeeperで仮想IPを付与するNICとILBのバックエンドプールとして指定するNIC(IP)を別にする必要があります。 https://docs.us.sios.com/sps/8.11.0/ja/topic/microsoft-azure-validation-guide?q=azure docs.us.sios.com   仮想IP実装に必要となる設定 Azureで仮想IPを機能させる場合、ロードバランサー、IPリソース、LBHCリソースの作成がポイントになります。 今回の検証にあたって作成した内容をそれぞれご紹介します。 ※WindowsOSで検証を行った際の内容となります。 ※環境により各設定値は変わる場合があります。 ロードバランサー作成 前提:仮想ネットワーク、仮想マシンの作成が完了していること。 Azure Portalの ホーム> 負荷分散とコンテンツ配信 > ロードバランサー の画面から[作成する]をクリックし、 [Standard Load Balancer]を選択 。 以下の通り各項目を記入、選択し、[次:フロントエンドIP構成>]をクリック。 ※ 今回は内部ロードバランサーを使用するため、[種類]は[内部]を選択。 [フロントエンドIP構成の追加]をクリックすると、画面右に「フロントエンドIP構成の追加」が表示されるため、 以下の通り各項目を記入、選択した後、[保存]⇒[次:フロントエンドIP構成>]をクリック。 ※ このクラスタへの仮想IPでの接続時は、この[IPアドレス]を仮想IPとして利用。 [バックエンドプールの追加]をクリック。 [バックエンドプールの追加]で以下の通り各項目を記入、選択し、[追加]をクリック。 画面右側に「バックエンドプールへのIP構成の追加」が表示されるため、バックエンドプールに指定するIPアドレスを選択し[追加]⇒[保存]をクリック。 ※ Windowsでは仮想IPリソースで使用するNICとは異なるNICのIPアドレスを選択。 [次:インバウンド規則>]をクリック。 [負荷分散規則の追加]をクリックすると画面右側に[負荷分散規則の追加]が表示される。 [正常性プローブ]で[新規作成]をクリックし、以下の通り各項目を記入し、[保存]をクリック。 ※ [ポート]は正常性プローブを行うポートの宛先となる。クラスタ内のノードで使用可能な任意のポートを指定。 画面右側の[負荷分散規則の追加]に以下の通り各項目を記入し、[保存]⇒[確認及び作成]をクリック。 デプロイを完了させる。 ※1、※2 クラスタ内のアプリケーションが使用するポート番号を指定。(今回は検証の為IISを導入するためポート80(HTTP)を指定。) ※3 クライアントからクラスターへ向けた通信を、IP リソース(VIP)宛のパケット通信とする必要があるため、[フローティングIP] は有効にする。   IPリソース作成 前提:OSの設定、LKインストール、コミュニケーションパスの作成が完了していること LifeKeeperのGUIで以下赤枠の[+]ボタンをクリックします。 ウィザードに従い、以下の表のとおり設定値を入力します。 IPアドレス(作成) プライマリサーバ LKNODE01   バックアップサーバ LKNODE02   保護するアプリケーション IPアドレス   IPアドレスのタイプ 仮想IPアドレス   IPアドレス 10.10.5.200 ※1 サブネットマスク 255.255.255.255 ※2 IPリソースタグ名 ip-10.10.5.200   ネットワーク接続 Ethernet 2 ※3 ローカル・リカバリー いいえ   IPアドレス(拡張) サブネットマスク 255.255.255.255 ※3 ネットワーク接続 Ethernet 2   ターゲット・リストアモード 有効   ターゲット・ローカルリカバリー いいえ   バックアップの優先順位 10   ※1 設定済の Azure ロードバランサーのフロントエンド IP と同じ値を使用。 ※2 ルーティングの競合を避けるため 255.255.255.255 を設定。 ※3 仮想IPを付与するNICとして、ILBのバックエンドプールに指定したNIC(IP)とは別のNICを指定。   LBHCリソース作成 前提:OSの設定、LKインストール、コミュニケーションパスの作成が完了していること LifeKeeperのGUIで以下赤枠の[+]ボタンをクリックします。 ウィザードに従い、以下の表のとおり設定値を入力します。 IPアドレス(作成) プライマリサーバ LKNODE01   バックアップサーバ LKNODE02   保護するアプリケーション LB Health Check   応答デーモンポート 12345 ※1 応答デーモンメッセージ 空欄   LB Health Checkリソースタグ名 lbhc-12345   IPアドレス(拡張) LB Health Checkリソースタグ名 lbhc-12345   バックアップの優先順位 10   ※1 ILBの[正常性プローブ]の[ポート]で設定した値を設定。   仮想IP機能検証 クラスタ内の各ノードでIISの導入およびIISリソース作成を行い、仮想IPの簡単な動作検証を行いました。 事前準備 クラスタ内各ノードのIISのドキュメントルートに、サーバ名を記載したテストページ(htmlファイル)を格納します。 <LKNODE01 %SystemDrive%\inetpub\wwwroot\test_page.html>      <LKNODE02 %SystemDrive%\inetpub\wwwroot\test_page.html>   動作確認 想定 :クライアント端末から仮想IPにアクセスすると、LKNODE01とLKNODE02のどちらがアクティブかスタンバイかに関係なく、常にアクティブ側のテストページが返される。 LKNODE01がアクティブ クライアント端末から仮想IP宛にIISのテストページにアクセスすると、想定通りLKNODE01のテストページが表示されました。     LKNODE02がアクティブ クライアント端末から仮想IP宛にIISのテストページにアクセスすると、想定通りLKNODE02のテストページが表示されました。   どちらも想定通りの結果となり、仮想IPが機能していることが確認できました! ちなみに… lbhcリソースを削除して、IISのテストページにアクセスしてみたところ、 lbhcリソースのヘルスプローブへの応答がなくなるためか、テストページに到達できなくなりました。     注意点 ここまでご紹介した仮想IPの実装に関して、以下点にご注意ください。 WindowsとLinuxでクライアントからの接続フロー・設計・設定が異なるため、事前のマニュアル確認を行う。 ■Windows   Azure 特有の設定について – LifeKeeper for Windows LIVE – 8.11.0 ■Linux   Azure 上の LifeKeeper 特有の設定について – LifeKeeper for Linux LIVE – 9.9.1 Windowsでは、LifeKeeperで仮想IPを付与するNICとILBのバックエンドプールとして指定するNIC(IP)を別にする。 IPリソース作成時、対象サブネット(本検証では10.10.5.0/24)に対するルーティングの競合を避けるため、 「255.255.255.255」で設定する。 クライアントからクラスターへ向けた通信を、IP リソース(VIP)宛のパケット通信とする必要があるため、 [フローティングIP] は有効にする。 (アクティブノードからの応答も仮想 IP から送信される) ホスト名に小文字が含まれている場合、LifeKeeper で不具合が発生する恐れがあるため、ホスト名は大文字で作成する。 ⇒ 当初ホスト名に小文字を含めてLifeKepperを構築していたところ、LBHCリソースの作成でエラー(No140321)が発生。   ホスト名を全て大文字にすることで事象解消。 OSの設定漏れに注意。 Azure固有のOS設定が必要のため、以下の対応漏れに注意する。 ■Windows    OS の設定 – LifeKeeper for Windows LIVE – 8.11.0 ■Linux    OSの設定 – LifeKeeper for Linux LIVE – 9.9.1   さいごに 今回はAzureにおいて、LifeKeeperとILBを活用して仮想IPを機能させる方法をご紹介しました。 本記事を通じて、Azureでも仮想IPを機能させることができ、障害時にもクライアントが意識することなくシステムを継続利用できることをご理解いただけましたら幸いです! 詳しい内容をお知りになりたいかたは、以下のバナーからSCSK Lifekeeper公式サイトまで
アバター
生成AIを活用した開発は急速に広がっています。しかし、いざ自分で取り組もうとすると「思った以上にハードルが高いのでは…」と感じたことはないでしょうか。 その背景には、 どこまでが自動化され、どこからが人間の役割なのかが直感的に分かりにくい という課題があります。その結果、「アプリを作るには結局どのくらいの知識が必要なのか」が見えづらく、最初の一歩が重くなってしまうのです。 誰もが試したいはずなのに…生成AIアプリ開発が“腰が重くなる”理由 生成AIアプリケーション開発に関連する知識領域 以下に、生成AIを活用したアプリケーション開発に関連する主な知識領域を独断と偏見で整理しました。 関連する知識ドメイン 内容  具体例 生成AI開発の専門用語と概念 開発現場では特有の用語や概念が飛び交い、それを理解する必要がある Claude Code、Gemini CLI、RAG、プロンプトエンジニアリング、MCP プログラミング  AIが生成したコードを扱うためには、基礎的なプログラミング素養が欠かせない 言語文法、アルゴリズム、ソフトウェア設計手法(構造化、OOP、関数型)、エラー処理、DB連携、主要フレームワーク(Rails、Reactなど) クラウドインフラ  実際にアプリを公開するには、クラウド環境を含めたシステム全体を理解する必要がある アプリ実行形式(Webアプリ、スマホアプリ、API)、データベース設計、ネットワーク/クラウド基盤、UI/UX設計 運用・非機能要件 他者に安心して使ってもらえる品質を維持するには、テストや運用設計が必須 各種テスト手法、CI/CD、各種非機能要件の確保(セキュリティ、性能など) これらをすべて学びきってからでないとアプリが作れない――そんな風に考えると、確かに厳しいですよね。   “第一歩の軽さ”をもたらすFirebase Studio こうして見ると、生成AI開発には多くの知識が関わっていて、最初の一歩が重く感じられます。 でも、すべてを理解してから始める必要はありません。本エントリでは、 Firebase Studio を使って、環境構築や複雑な設定を最小限に、アイデアをすぐに形にし、アプリケーションとしてデプロイしていきます。 今回は、結婚式やパーティー会場で使える 写真共有アプリケーション を作っていきます。 もちろん、Firebase Studioを使えばもっと高機能なアプリケーションも作れますが、ここでは説明を分かりやすくするため、あえてシンプルな構成にしています。 このアプリは、 PythonやJavaScriptのプログラミングスキルがなくても構築でき、必要に応じて拡張していくことも可能 です。 興味が湧いてきましたか? では、さっそく作り方を見ていきましょう。 Webページとしてはちょっと縦長ですが、作業はほぼ一本道で進みます。   Step.1 まず、アプリケーションから作る 最初に行うのは、細かいプログラミングではなく 動くアプリケーションを用意すること です。 Firebase Studio ( https://studio.firebase.google.com/ ) にアクセスします。 初回はこのようなメッセージが表示されるので terms and conditions を受け入れて次の画面に行きます。 Firebase Studioの画面が表示されます。 (沢山アプリを作っているのでアプリ名などを加工して見えなくしています) 画面のように左ウィンドウにプロンプトを入力し、「Prototype with AI」ボタンを押しましょう。 入力したプロンプトは以下の通りです。 以下の機能を持つ、パーティー用のアルバムWEBアプリを作成してください。 写真を登録、削除する機能 複数の写真を一覧できるサムネイル機能 個々の写真を拡大して表示する機能 複数の写真を自動で切り替えるスライドショー機能 UIは、お祝いの場にふさわしい、明るくエレガントなデザイン リコメンド系の機能は不要 Firebase Studioはすぐにアプリケーションの構築プランを用意してくれます。 「Prototype this App」ボタンを押すと、すぐにAIがアプリケーションを開発し始めます。 わずか13秒後にアプリケーションが動き始めます。早い! 左側のウィンドウにアプリケーションが表示されるので、すぐに動かして動作検証ができます。 画像アップロードももちろん成功。画像の拡大やスライドショーだってできます。   アプリケーションが出来上がってしまいました。 ただし、このアップロードしたデータはまだどこにも保存されていないため、画面を再表示すると消えてしまいます。データをクラウド上のどこかに永続的に保存する必要があります。 そこで次のステップでは、 Firebaseプロジェクトの各種設定 を行います。具体的には、データを保存するための Firestore と Storage の設定、そしてアプリを外部に公開するための Firebase App Hosting の有効化とデプロイ手順について扱っていきます。   Step.2 アプリを動かすFirebaseを設定する アプリケーションを Firebase App Hosting にデプロイして公開します。 Step.2-1 Storageの設定 やりたい事は言葉(プロンプト)にしてAIに伝える、というのが基本です。   FirebaseプロジェクトをAI側で設定してくれました。 しかし、まだこの段階ではエラーが出ます。 エラーを渡してみます。 この後、Storageの権限設定を開始します。 Firebaseコンソール( https://console.firebase.google.com/ )の話が出てきましたので、アクセスします。 今回作ったアプリケーションが表示されているのでアクセスします。 左側の「構築」→「Storage」を選択するとStorageの画面が表示されます。 「プロジェクトをアップグレード」という文に、課金圧を感じますが課金の手続きをしましょう。課金プラン(Blazeプラン)については後述します。手順の詳細は省きますが、クレジットカードでの設定が便利です。 こんな感じで予算額と、それを超えた場合のメール通知も設定できます。仲間内で使う分には3000円、行かないと思いますが・・・。 Blazeプランの設定が終わったので、「使ってみる」を選択します。 保存先のロケーションにはこだわりが無いので、ここは「US-CENTRAL1」で セキュリティルールはデフォルトで提示されたもの(本番環境モード)でよいです。 おそらく後からAIに修正案を出してもらう可能性が高いです。もし、AIに修正案を提示してもらわなくても済んでしまった場合に備えて、厳しめに設定しておくことを推奨します。 この後、AIから修正を依頼された場合はFirebaseコンソールの「Storage」から、画面右側の「ルール」タブを選択しましょう。こちらからルールを編集できます(アプリの情報を開示しないため、白く塗りつぶしています)。 Storageの設定を完了すると、アプリケーションが動くはずですが、もしかするとコーディングエラーが発生しているかもしれません。 エラーが発生した時は、以下のような表示が出ることが多いので「Fix Error」しましょう。ボタンが出ないときは、エラー内容を入力欄に張り付ければ対応してもらえます。 これで動くようになりました。 AIは決定論的に毎回同じ挙動をするわけではないので、違う流れで進むかもしれませんが、おおむねこのような流れで作業を行うとアプリケーションが完成します。 Storageの設定完了により、データがクラウド上に保存されるようになりました。 Step.2-2 Firebase App Hostingへのデプロイ あとは、「Firebase App Hosting」を使ってアプリケーションをクラウド上にデプロイするだけです。ゴールはもうすぐ。画面の右上を見てください。「Publish」というボタンが見えるはずです。 ここまでの設定ができていれば、このボタンを押して表示される命令をすべて対応すればデプロイが完了します。 「Set up services」を押せば「Firebase App Hosting」にアプリケーションがデプロイされ、晴れてWebアプリケーションとして動作することになります。お疲れさまでした!   まとめ 取り扱った内容 ここまでで、Firebase Studioを使って 写真共有アプリケーションを作成 し、さらに Firebaseプロジェクトの設定 を通じて以下を整えました。 Firebase Studio を利用して アプリケーションを構築 Firebase Storage を利用して 実際の画像ファイルをクラウドに保存 Firebase App Hosting を利用して Webサイトとしてアプリを公開 今後について 今回暑かったのは「パーティー用の写真共有アプリ」でした。つまり、共有範囲を限定でき、問題が発生した時の影響が小さいことが前提のアプリです。これを本格的に他の人が使えるアプリにするためには様々な考慮が必要です。 Firebase Studioでプロンプトベースの開発を進めていくうえで、知っておくべきこと、知っておいた方が良い事について、今後エントリを上げていきますので、よろしくお願いいたします。 伝えたい事 Firebase Studioで実際にアプリを作り始めてみると、次第に「 プロンプトの工夫が必要だ 」とか「 AIが生成したコードの不具合にどう対処するか 」といった課題に気づくはずです。 ただし、これらは事前に机上で考えすぎても答えが出にくいものです。 一度手を動かして作ってみることで初めて分かることが多い 、というのが正直な感想です。 その点、Firebase Studioは入口の敷居がとても低く、誰でもすぐに「作ってから学ぶ」というサイクルに入ることができます。 せっかくこれほど便利で強力な環境が整っているのですから、ぜひまずは一度、手元でアプリを作ってみませんか
アバター
CloudWatchで監視を実装している環境に対して、夜間バッチ処理中はアラームを抑制することで、不要な通知を避けたいということはよくあることかと思います。 本記事では、CloudWatchのMetric Mathを活用して、特定の時間帯だけアラームを抑制する設定を行った実践例をご紹介します。 Metric Mathを採用した理由 まずはじめにCloudWatch監視における非監視設定の選択肢について考えました。 非監視時間に合わせてCloudWatchアラームを無効化/有効化する CloudWatch アラームとSNS トピックの間にLambda関数を仕込み、時間をチェックし非監視時間帯であれば通知を抑止するよう作りこむ MetricMath関数を使って、非監視時間帯のメトリクスを正常メトリクスとして上書きする 今回、非監視時間帯に本当に発生したアラートは、非監視時間帯明けに通知させたいという要件がありました。 CloudWatchではSNSやLambdaなど、アラートを検知した際に他サービスと連携することができますが、これはアラームの状態が遷移したことをトリガーにするため、非監視時間帯にアラート状態になったアラームは非監視時間帯が明けても通知されず、アラートに気づかないことがあるわけです。 監視明けのアラームの状態をチェックするにはまたLambda作りこみが必要となり、面倒くさい・・・。 MetricMath関数では、関数の通りにメトリクスの値を上書きできるイメージのため、非監視時間帯はわざと正常な値で上書き、非監視時間帯が明けたら実際の値を取得します。 非監視時間帯が明けたタイミングでアラーム状態に遷移してくれるので、楽に実装できそうとなり、MetricMath関数を使ってみることにしました。   Metric Mathとは? CloudWatchのメトリクス(CPU使用率、ディスクI/O、リクエスト数など)に対して、加算・減算・乗算・除算・統計関数などを使って計算を行うことができます。 例えば、 複数のEC2インスタンスのCPU使用率の平均を出す リクエスト数とエラー数からエラー率を計算する カスタムメトリクスを組み合わせて複雑な条件でアラームを設定する みたいなことができます。 よく使われる関数 関数 説明 SUM([m1, m2]) メトリクスm1とm2の合計 AVG([m1, m2]) 平均値 MAX([m1, m2]) 最大値 MIN([m1, m2]) 最小値 RATE(m1) 単位時間あたりの変化率(カウンター系メトリクスに便利) IF(condition, value_if_true, value_if_false) 条件分岐 CloudWatch メトリクスでの数式の使用 – Amazon CloudWatch   やってみた 今回は特定の時間帯だけアラームを抑制したいので、タイムスタンプに基づいて値を返してくれる関数を利用します。 関数 説明 MINUTE 元の時系列の各タイムスタンプで UTC 分を表す、非スパース時系列 (0~59 の整数) を返します。 HOUR 元の時系列の各タイムスタンプで UTC 時間を表す、非スパース時系列 (0~23 の整数) を返します。 DAY 元の時系列の各タイムスタンプで UTC 曜日を表す、非スパース時系列 (1~7の整数) を返します。1 は月曜日を、7 は日曜日を表します。 DATE 元の時系列の各タイムスタンプで UTC 日付を表す非スパース時系列 (1~31の整数) を返します。 今回は毎晩2:00~4:00の間アラームを抑制してみます。 タイムスタンプは UTC 基準のため、UTC17:00~19:00を関数で表現します。 IF(AND(HOUR(m1) >= 17, HOUR(m1) < 19), 10, m1) HOUR(m1)  はメトリクス  m1  のタイムスタンプの「時」を取得(UTC) 13 <= HOUR < 17 の時間帯(17:00〜18:59)に該当する場合、値を 10  に上書き それ以外の時間帯は元のメトリクス  m1  の値を使用 MetricMath関数を使ったメトリクスとアラームを作成 対象のメトリクスを選択し「数式を追加」をクリックします。 [すべての関数]-[IF]を選択。 追加されたメトリクスの[詳細]欄の編集マークをクリックし、設定したい関数を入力し、「適用」をクリック IDやラベルは任意で設定してください。 作成したメトリクスの鈴マークをクリック お好みでアラーム設定   作成したメトリクスを確認してみます。 このメトリクスはTomcatのプロセス数を取得しており、本来の値は1が記録されています。 グラフから2:00~3:59は本来のメトリクス値でなく、一律10という値で記録されていることがわかります。 監視設定ではメトリクスが0になったらアラート状態になるよう設定しましたので、2:00~3:59の間はアラート状態にはなりません。 2:00~3:59にアラート状態になった場合も、2:00~3:59ではなく非監視時間帯が明けた4時に通知されました。 ※非監視時間内に正常に戻った場合は通知されません。   さいごに Lambda等その他のサービスを使うことなく、非監視設定できたのは大変ありがたいです。 Lambdaの管理も必要なく運用負担も軽減できる点も嬉しいですね。  
アバター