CFNカスタムリソースを使用して、OpenSearch Serverlessのインデックス作成を自動化する

CloudFormationカスタムリソースを使用して、OpenSearch Serverlessのインデックス作成を自動化する

CFNカスタムリソースを使用して、OpenSearch Serverlessのインデックス作成を自動化する

以下のページでOpenSearch Serverlessについて取り上げました。

あわせて読みたい
CloudFormationを使用して、OpenSearch Serverlessを作成する 【CloudFormationを使用して、OpenSearch Serverlessを作成する】 本ページでは、OpenSearch Serverlessを取り上げます。 Amazon OpenSearch Serverless は、Amazon Ope...

上記のページでは、手動でLambda関数を実行することで、インデックスを作成しました。

本ページでは、自動的にインデックスを作成する方法を考えます。
具体的には、CloudFormationカスタムリソースを使用する方法です。

構築する環境

Diagram of automate OpenSearch Serverless indexing using CloudFormation Custom Resources.

CloudFormationを使用して、主に2つのリソースを作成します。

1つ目はOpenSearch Serverlessコレクションです。

2つ目はLambda関数です。
関数のランタイム環境はPython3.8です。
この関数をCloudFormationカスタムリソースに関連付けて、スタック作成時に本関数が実行されるように設定します。
この関数の働きは、OpenSearch Serverlessにインデックスを作成することです。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-fa/tree/main/148

テンプレートファイルのポイント解説

Lambda関数

Resources:
  CustomResource2:
    Type: Custom::CustomResource
    DependsOn:
      - Function2
    Properties:
      ServiceToken: !GetAtt Function2.Arn
Code language: YAML (yaml)

CloudFormationカスタムリソースの本体です。
後述のLambda関数を指定し、CloudFormationスタック操作時に、同関数を実行できるようになります。

CloudFormationカスタムリソースに関しては、以下のページをご確認ください。

あわせて読みたい
CloudFormationカスタムリソース入門 【CloudFormationカスタムリソースの挙動を確認する構成】 CloudFormationの機能の1つにカスタムリソースがあります。 カスタムリソースを使用すると、テンプレートにカ...
Resources:
  Function2:
    Type: AWS::Lambda::Function
    Properties:
      Architectures:
        - !Ref Architecture
      Environment:
        Variables:
          COLLECTION_ENDPOINT: !Sub "${Collection}.${AWS::Region}.aoss.amazonaws.com"
          OPENSEARCH_INDEX_NAME: !Ref OpenSearchIndexName
          REGION: !Ref AWS::Region
      Code:
        ZipFile: |
          from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth
          import boto3
          import cfnresponse
          import os

          host = os.environ['COLLECTION_ENDPOINT']
          opensearch_index_name = os.environ['OPENSEARCH_INDEX_NAME']
          region = os.environ['REGION']

          service = 'aoss'
          credentials = boto3.Session().get_credentials()
          auth = AWSV4SignerAuth(credentials, region, service)

          CREATE = 'Create'
          response_data = {}

          client = OpenSearch(
            hosts=[{'host': host, 'port': 443}],
            http_auth=auth,
            use_ssl=True,
            verify_certs=True,
            connection_class=RequestsHttpConnection,
            pool_maxsize=20,
            )

          def lambda_handler(event, context):
            try:
              if event['RequestType'] == CREATE:
                create_response = client.indices.create(
                  opensearch_index_name
                )
                print(create_response)

              cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)

            except Exception as e:
              print(e)
              cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
      FunctionName: !Sub "${Prefix}-function-02"
      Handler: !Ref Handler
      Layers:
        - !Ref LambdaLayer
      Runtime: !Ref Runtime
      Role: !Ref FunctionRoleArn2
      Timeout: 60
Code language: YAML (yaml)

CloudFormationカスタムリソースに関連づけるLambda関数です。

if文を使用して、CloudFormationスタック作成時に、以降のコードが実行されるように設定します。

OpenSearch用のクライアントオブジェクトを作成し、同オブジェクトのindices.createメソッドを実行して、インデックスを作成します。
作成するインデックスの名前は「python-test-index」です。
この名前の文字列はに環境変数として関数に渡します。

PythonからOpenSearch Serverlessにアクセスするために、AWS公式が提供しているパッケージ(opensearch-py)を使用します。
このパッケージはデフォルトのLambda関数のランタイム環境には含まれていません。
今回はLambdaレイヤーを作成し、これを含めることによって、同パッケージを使用できるようにします。

詳細につきましては、冒頭でご紹介したページをご確認ください。

CloudFormationカスタムリソースを使用して、自動的にこのLambdaレイヤーを作成します。
詳細につきましては、以下のページをご確認ください。

あわせて読みたい
CFNカスタムリソースでLambdaレイヤーパッケージを準備する – Python版 【CloudFormationカスタムリソースを使って、Python用のLambdaレイヤーパッケージを自動的に作成・配置する】 以下のページでLambdaレイヤーの作成方法について取り上げ...
Resources:
  FunctionRole2:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: FunctionRole2Policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - aoss:APIAccessAll
                Resource:
                  - !Sub "arn:aws:aoss:${AWS::Region}:${AWS::AccountId}:collection/*"
      RoleName: !Sub "${Prefix}-FunctionRole2"
Code language: YAML (yaml)

Lambda関数用のIAMロールです。

OpenSearch Serverlessへの全APIアクセスを許可する内容です。

(参考)IAMユーザ

Resources:
  User:
    Type: AWS::IAM::User
    Properties:
      LoginProfile:
        Password: !Ref Password
      Policies:
        - PolicyName: AllAllowPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - aoss:*
                Resource: "*"
      UserName: !Sub "${Prefix}-user"
Code language: YAML (yaml)

OpenSearch Serverlessダッシュボードを操作するための専用ユーザです。
インラインポリシーでOpenSearch Serverless系の全アクションを許可します。

(参考)OpenSearch Serverless

Resources:
  Collection:
    Type: AWS::OpenSearchServerless::Collection
    DependsOn:
      - EncryptionSecurityPolicy
    Properties:
      Name: !Ref CollectionName
      StandbyReplicas: DISABLED
      Type: TIMESERIES

  DataAccessPolicy1:
    Type: AWS::OpenSearchServerless::AccessPolicy
    Properties:
      Name: !Sub "${Prefix}-data-policy-01"
      Policy: !Sub >-
        [{"Description":"Access for cfn user","Rules":[{"ResourceType":"index","Resource":["index/*/*"],"Permission":["aoss:*"]},
        {"ResourceType":"collection","Resource":["collection/${CollectionName}"],"Permission":["aoss:*"]}],
        "Principal":["${UserArn}"]}]
      Type: data

  DataAccessPolicy2:
    Type: AWS::OpenSearchServerless::AccessPolicy
    Properties:
      Name: !Sub "${Prefix}-data-policy-02"
      Policy: !Sub >-
        [{"Description":"Access for Function2","Rules":[{"ResourceType":"index","Resource":["index/${CollectionName}/${OpenSearchIndexName}"],"Permission":["aoss:CreateIndex"]}],
        "Principal":["${FunctionRoleArn2}"]}]
      Type: data

  NetworkSecurityPolicy:
    Type: AWS::OpenSearchServerless::SecurityPolicy
    Properties:
      Name: !Sub "${Prefix}-network-policy"
      Policy: !Sub >-
        [{"Rules":[{"ResourceType":"collection","Resource":["collection/${CollectionName}"]},
        {"ResourceType":"dashboard","Resource":["collection/${CollectionName}"]}],"AllowFromPublic":true}]
      Type: network

  EncryptionSecurityPolicy:
    Type: AWS::OpenSearchServerless::SecurityPolicy
    Properties:
      Name: !Sub "${Prefix}-encryption-policy"
      Policy: !Sub >-
        {"Rules":[{"ResourceType":"collection","Resource":["collection/${CollectionName}"]}],"AWSOwnedKey":true}
      Type: encryption
Code language: YAML (yaml)

OpenSearch Serverlessおよび各種ポリシーを作成します。

OpenSearch Serverlessに関する基本的な事項については、冒頭でご紹介したページをご確認ください。

ポイントはデータアクセスポリシーです。
2つ作成します。

1つ目は専用IAMユーザ用のポリシーです。
ユーザに全アクションを許可する内容です。

2つ目はLambda関数用のポリシーです。
インデックス作成に必要なアクション(aoss:CreateIndex)を許可する内容です。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

今回は名前をつけるIAMリソース(IAMユーザ等)を作成しますから、以下の通りにオプションを設定します。

$ aws cloudformation create-stack \
--stack-name [stack-name] \
--template-url https://[bucket-name].s3.ap-northeast-1.amazonaws.com/[folder-name]/fa-148.yaml \
--capabilities CAPABILITY_NAMED_IAM
Code language: Bash (bash)

AWS Management ConsoleからOpenSearch Serverlessコレクションを確認します。

Detail of OpenSearch 01.

OpenSearch Serverlessコレクションが正常に作成されています。

データアクセスポリシーを確認します。

Detail of OpenSearch 02.
Detail of OpenSearch 03.

前者はIAMユーザ用で、2つ目はLambda関数用です。
それぞれの用途に応じたアクションが許可されています。

Lambda関数を確認します。

Detail of Lambda 01.

Lambda関数が正常に作成されています。

Lambdaレイヤーを見ると、レイヤーが作成されており、この関数に関連づいていることがわかります。
CloudFormationカスタムリソースによって、自動的にLambdaレイヤーが作成されたということです。

動作確認

準備が整いましたので、CloudWatch Logsにアクセスして、Lambda関数の動作状況を確認します。

Detail of Lambda 02.

関数が自動的に実行されていることがわかります。
つまりCloudFormationカスタムリソースによって、自動的に本関数が実行されたということです。

専用IAMユーザでOpenSearch Serverlessダッシュボードにログインします。
ダッシュボードからインデックスを確認します。

Detail of OpenSearch 04.

確かに「python-test-index」が作成されていることがわかります。
このことから、確かにCloudFormationカスタムリソースによってLambda関数が実行されて、OpenSearch Serverlessにインデックスが作成されたことがわかります。

まとめ

CloudFormationカスタムリソースを使用して、OpenSearch Serverlessに自動的にインデックスを作成する方法をご紹介しました。