【初心者向け】ゼロから始めるAWSでの生成AI開発(Amazon Bedrcok) アカウント初期セットアップ編

先日、AWSのAmazon Bedrcokを利用した生成AI開発のPJの立ち上げをしました。
その際に、新しいAWSアカウントの初期セットアップを行ったので、その時のノウハウをまとめました。

今回紹介する作業の流れは下記になります。
①IAMユーザの設定　MFAデバイス
②コストアラートの設定
③AWS CloudTrailでの操作ログ取得
④Amazon GuardDutyで不正アクティビティ検知の実装
⑤Amazon Bedrcokでモデルアクセス有効化
ぶっちゃけたことを言うと⑤の設定だけやれば、Amazon Bedrcokは利用可能です。
ですが、業務／会社のAWSアカウントで利用となると、不正利用を防ぐためのセキュリティ対策、課題なAWS使用料金の発生を防ぐための対策は必須なので、必ず設定を入れるようにしましょう。
また、定期的にAWS利用料金(コスト)をチェックするのも推奨です。
※以前ネットで見つけたBedrockのナレッジベースのデモをベースに作ったら、
　OpenSearchを起動していて、月5万円近いサブスク契約になってました。
　コストチェックで気づいて慌ててナレッジベースのリソース削除を実施しました。

## ①IAMユーザの設定　MFAデバイス
まずは、利用するIAMユーザの設定です。IAMユーザの作成方法、適切なロール、ポリシーの設定は基本中の基本なので説明は割愛します。
確実に設定を入れておきたいのは二段階認証(MFA)の設定です。
各ユーザ毎にAWSコンソールにログインした後、IAM-ユーザ-[自分のユーザ]の設定から「MFAデバイスの割り当て」から設定を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/5f16048e-92f9-4b69-b223-727ec20d93b4.png)
また、MFAデバイスを登録してないIAMユーザにAWSの操作が不可の設定を入れておきましょう。
設定方法は、下記のJSONコードでIAMポリシーを設定します。(名前は
nonMFA_deny)
補足すると、MFAデバイスの変更を許可する権限、MFAデバイス設定が入って無いとその他のAWS操作をすべて拒否する権限が付与されてます。
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice"
            ],
            "Resource": "arn:aws:iam::*:mfa/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:DeactivateMFADevice",
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice",
                "iam:ListMFADevices",
                "iam:ChangePassword"
            ],
            "Resource": "arn:aws:iam::*:user/${aws:username}"
        },
        {
            "Effect": "Deny",
            "NotAction": [
                "iam:CreateVirtualMFADevice",
                "iam:DeleteVirtualMFADevice",
                "iam:DeactivateMFADevice",
                "iam:EnableMFADevice",
                "iam:ResyncMFADevice",
                "iam:ListMFADevices",
                "iam:ListVirtualMFADevices",
                "iam:ChangePassword"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
}
```
次にIAMユーザグループを作成します。先ほど作成したIAMポリシー(nonMFA_deny)を紐づけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/fd2c5c80-0486-466f-90b0-f52ca595eb14.png)
最後に、IAMユーザグループにIAMユーザを紐づけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/5743c52a-252f-471e-9f9a-085bdb7ea9bf.png)


## ②コストアラートの設定
AWS利用料金が多くなり過ぎないよう事前に予算部門と調整した金額を超えそうな場合、コストアラートを発行するようにします。



コストアラームの設定方法は下記になります。
「Billing and Cost Management」の画面のAWS Budgets(予算)の項目からコストアラートを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/cb09b391-5e17-4615-a8b9-e89508e62334.png)
「予算を作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/56c62b6b-6567-43c7-a687-d8877fc220f2.png)
各種設定項目を入力します。
今回のPJでは月100USD迄利用可能のルールになっていたので、100USDに設定しています。
メールアドレスの欄にはコストアラートの通知先のアドレスを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/609dd853-2575-4d03-bd11-897832d36348.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/69971f4e-5e3f-4ac1-8ce3-a6a3188dbdd6.png)
設定を入力したら「予算を作成」をクリックして、コストアラートを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/5faf7b59-c57c-4640-85c8-c795816b32a7.png)
これでコストアラートの作成は完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/afa846e3-881b-4f8d-9479-cc8407613181.png)

## ③AWS CloudTrailでの操作ログ取得
AWS CloudTrailはAWS上での操作を記録した監査ログになります。
AWSの設定が不正に変更された、不要なリソースを作成した人がいた時の、犯人捜しに非常に役立つので有効化しておきます。
デフォルトの設定では、90日間しか保存されていない設定のため、CloudTrailログをS3に保存して、長期間ログを参照できる設定にしました。
CloudTrailログを作成するためのCloudFormationのyamlファイルのサンプルを添付します。
```
AWSTemplateFormatVersion: '2010-09-09'

Description: This template create CloudTrail.

Parameters:
  
  Env:
    Description: select the environment name.
    Type: String
    Default: env1
    AllowedValues:
      - env1
      - env2
      - env3
  
  EnvLowerCase:
    Description: select the environment name.
    Type: String
    Default: env1
    AllowedValues:
      - env1
      - env2
      - env3
        
  SystemName:
    Type: String
    MinLength: 1
    MaxLength: 100
    Default: CLOUD TRAIL
  
  SystemNameLowerCase:
    Type: String
    MinLength: 1
    MaxLength: 100
    Default: cloudtrail
  
  CloudTrailLogGroupName:
    Description: select CloudTrail LogGroupName.
    Type: String
    Default: "CloudTrail/DefaultLogGroup"
    

Resources:

  # ------------------------------------------------------------------
  #  Create S3 Bucket & Policy for CloudTrail
  #  - BucketName                     : バケット名
  #  - AccessControl                  : バケットへのACL（デフォルトは Private: 所有者以外にはアクセス許可あり）
  #  - BucketEncryption               : バケットの暗号化 ※SSE-S3の場合AES256　SSE-KMSの場合KMS
  #  - LifecycleConfiguration         : バケットのライフサイクル
  #    - Status                       : ルールの有効化
  #    - ExpirationInDays             : 削除されるまでの経過日数
  #    - Transitions                  : s3標準から別のストレージクラスへ移行されるルール
  #  - PublicAccessBlockConfiguration : すべてブロックでパブリックアクセスをすべてブロック
  #  + DeletionPolicy                 : Retain＝スタック削除してもリソースやコンテンツを削除せず保持します。
  #  
  # ------------------------------------------------------------------
  
  CloudTrailS3Bucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub "${SystemNameLowerCase}-${EnvLowerCase}-s3-cloudtrail-bucket"
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      ObjectLockEnabled: true
      ObjectLockConfiguration:
        ObjectLockEnabled: Enabled
        Rule: 
          DefaultRetention:
            Mode: GOVERNANCE
            Days: '2563'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      LifecycleConfiguration:
        Rules:
        - Id: !Sub "${SystemNameLowerCase}-${Env}-s3-lifecycle-7years-delete"
          Prefix: ''
          Status: Enabled
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: '7'
          ExpirationInDays: '2563'
      Tags: 
        - Key: "Name"
          Value: !Sub "${SystemNameLowerCase}-${Env}-s3-cloudtrail-bucket"
      LoggingConfiguration:
        DestinationBucketName: !Sub "${SystemNameLowerCase}-${EnvLowerCase}-s3-cloudtrail-bucket"
        LogFilePrefix: !Sub "s3://${SystemNameLowerCase}-${EnvLowerCase}-s3-cloudtrail-bucket/accesslog"

  CloudTrailBucketPolicy: 
    Type: AWS::S3::BucketPolicy
    Properties: 
      Bucket: !Ref CloudTrailS3Bucket
      PolicyDocument:
        Id: CloudTrailBucketPolicy
        Version: 2012-10-17
        Statement:
          - Sid: AWSCloudTrailAclCheck
            Effect: Allow
            Principal: 
              Service: cloudtrail.amazonaws.com
            Action: 's3:GetBucketAcl'
            Resource: 
              - !Join [ "", [ 'arn:aws:s3:::', !Ref CloudTrailS3Bucket ] ]
          - Sid: AWSCloudTrailWrite
            Effect: Allow
            Principal: 
              Service: cloudtrail.amazonaws.com
            Action: 's3:PutObject'
            Resource:
              - !Join [ "", [ 'arn:aws:s3:::', !Ref CloudTrailS3Bucket, !Sub "/AWSLogs/${AWS::AccountId}/*"] ]
            Condition:
              StringEquals:
                's3:x-amz-acl': 'bucket-owner-full-control'

  CloudTrailIAMRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Effect: "Allow"
          Principal:
            Service:
            - "cloudtrail.amazonaws.com"
          Action:
          - "sts:AssumeRole"
      Policies:
        - PolicyName: !Sub "${SystemNameLowerCase}-${Env}-iam-policy-CloudTrailPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
            - Effect: "Allow"
              Action:
              - "logs:CreateLogStream"
              Resource: !Join [ "", [ "arn:aws:logs:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":log-group:", !Ref CloudTrailLogGroupName, ":log-stream:", !Ref "AWS::AccountId", "_CloudTrail_", !Ref "AWS::Region", "*" ] ]
            - Effect: "Allow"
              Action:
              - "logs:PutLogEvents"
              Resource: !Join [ "", [ "arn:aws:logs:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":log-group:", !Ref CloudTrailLogGroupName, ":log-stream:", !Ref "AWS::AccountId", "_CloudTrail_", !Ref "AWS::Region", "*" ] ]
      Path: "/"
      RoleName: !Sub "${SystemNameLowerCase}-${Env}-iam-role-CloudTrailRole"

  CloudTrail: 
    DependsOn: 
      - CloudTrailS3Bucket
      - CloudTrailIAMRole
      - CloudTrailBucketPolicy
    Type: AWS::CloudTrail::Trail
    Properties: 
      S3BucketName: !Ref CloudTrailS3Bucket
      EnableLogFileValidation: true
      IncludeGlobalServiceEvents: true
      IsLogging: true
      IsMultiRegionTrail: true
      TrailName: !Sub "${SystemNameLowerCase}-${Env}-cloudtrail"
      Tags: 
        - Key: "Name"
          Value: !Sub "${SystemNameLowerCase}-${Env}-cloudtrail"
```

CloudFormationのコードを実行した後、作成した証跡に、S3,Lambda、DynamoDBのデータイベントの証跡の記録の設定を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/c478e60c-14fc-4aba-838e-f51d760f9f35.png)

## ④Amazon GuardDutyで不正アクティビティ検知の実装
Amazon GuardDutyは各種AWSサービスの不正利用を検知するサービスのため有効化します。
BedrockがAPI経由で、不正に実行されてる場合の検知等ができるようになります。
Amazon GuardDutyの仕様や実装の説明については、以前下記のブログで触れてるので、今回詳細は割愛します。
https://qiita.com/tkazuaki0820/items/be667159134b7614b9e5

## ⑤Amazon Bedrcokでモデルアクセス有効化
LLMの基盤モデルをAmazon Bedrcokから利用できるようにモデルアクセスを有効化します。
※この作業は新しい基盤モデルの利用を開始するたびに必要な作業なので、必ず覚えてください。
また、この作業はリージョン毎に設定が必要です。Amazon Bedrcokを使用するすべてのリージョンで設定を行ってください。
モデルアクセスの有効化はAmazon Bedrcokのモデルアクセスのページから行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/0299add2-4fdb-4e23-a27e-c83b97f0ff0f.png)

「特定のモデルを有効化にする」を選択するを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/c745f0cc-3d59-48d4-b35a-580e1aca040c.png)

有効化したい基盤にチェックを入れて、「次へ」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/2947dd35-d3a2-4f8d-9f23-4858b22b9de8.png)
ここで一つ補足です。「Cross-region inference」と記載がされてるモデルはクロスリージョン推論を利用しての提供になります。
AWSコンソールで表示されてるリージョンとは別のリージョンにある基盤モデルで実行される可能性があります。
セキュリティ要件で指定のリージョンしか利用できない制約がある場合は、クロスリージョン推論を利用してるモデルは選択しないようにしましょう。
クロスリージョン推論の詳細は下記のAWS公式サイトに記載があります。
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/cross-region-inference.html

Anthropic社が提供してるモデル(Claude)を選択すると下記の入力を求められるので、必要な情報を入力して、「次へ」をクリックしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/ad0c3300-d255-4d78-9aa8-f27dd3295fd2.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/7746692c-caf8-4b2a-a402-9ab781ea7469.png)

確認して送信の画面が表示されるので、「送信」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/5b3082f1-1da4-455d-8afa-bad651252cda.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/5387326a-d337-486f-969c-bfeb57f418b2.png)

各基盤モデルに「アクセスが付与されました」と表示されれば、利用可能な状態になります。
※利用可能になるまで、数分かかる場合あります。また、AWSからも何通かメールが届きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/a7321802-09ec-4eed-85b4-d3923754be39.png)

playgroundから有効化したモデルが利用可能になってることを確認します。
今回は「Chat / Text playground」の画面から確認してます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3990360/5288ba5f-5de2-4d3c-a420-d52e7c90df67.png)

## 最後に
今回はAWSでの生成AI開発(Amazon Bedrcok)を始める際の、アカウント初期セットアップの手順を解説しました。
かなり長文の説明になってしまいましたが、AWS慣れてる人がやれば半日もあれば余裕で終わるボリュームです。