👻

Opensearchでnetwork policyをprivateに限定するとterraformでindexが作成できない

に公開

OpenSearch Serverless をTerraformでマネージしたい。
Collection、ネットワークポリシー、データポリシーなど基本的な要素はTerraformのawsのプロパイダに含まれるが、awsプロバイダではindexが作成できない。

そこでOpenSearchプロバイダを入れて、以下のようにopensearch_indexリソースを使ってindexを定義することになる。

# Configure the OpenSearch provider
provider "opensearch" {
  url = "http://127.0.0.1:9200"
}

resource "opensearch_index" "test-simple-index" {
  name               = "terraform-test"
  number_of_shards   = "1"
  number_of_replicas = "1"
  mappings           = <<EOF
{
  "properties": {
    "name": {
      "type": "text"
    }
  }
}
EOF
}

resource "opensearch_index" "index" {
  name = "sample"
  aliases = jsonencode(
   {
    "log": {
      "is_write_index": true
    }
  }
  )
  number_of_replicas = "1"
  number_of_shards = "1"
  mappings = jsonencode({
    "properties": {
      "age": {
        "type": "integer"
      }
    }
  })
}

引用:
https://registry.terraform.io/providers/opensearch-project/opensearch/latest/docs/resources/index

これはnetwork policyがPublicの場合はうまくいくが、privateにするとエラーになる。
(※Googleで検索すると、network policyをPublicにした例ばかり出るので注意が必要)

Network PolicyをPrivateにする場合、接続元を許可したVPCE経由でないとIndexの作成ができないので、Indexを作成するためのLambdaを用意してInvokeする設計にする。
↓のLambdaをVPCEを接続したSubnetに配置

Lambda関数

import boto3
from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth

def lambda_handler(event, context):
    auth = AWSV4SignerAuth(credentials, region_name, 'aoss')
    client = OpenSearch(
        hosts=[{'host': endpoint, 'port': 443}],
        http_auth=auth,
        use_ssl=True,
        verify_certs=False, 
        connection_class=RequestsHttpConnection,
        timeout=10,
        max_retries=3,
        retry_on_timeout=True
    )
    
    index_body = {
        "mappings": {
            "properties": {
                "vector": {
                    "type": "knn_vector",
                    "dimension": 1024
                },
                "text": {
                    "type": "text"
                },
                "metadata": {
                    "type": "text"
                }
            }
        }
    }
    
    response = client.indices.create(
        index='test_index',
        body=index_body
    )

Terraform

data "archive_file" "lambda_function" {
  type        = "zip"
  source_dir  = var.lambda_source_dir
  output_path = "${var.lambda_source_dir}/../build/lambda_function.zip"
}

resource "aws_lambda_function" "lambda" {
  filename         = data.archive_file.lambda_function.output_path
  source_code_hash = data.archive_file.lambda_function.output_base64sha256
  function_name    = "index_creater"
  role             = var.lambda_role_arn
  handler          = "main.lambda_handler"
  runtime          = "pyhon3.11"
  timeout          = 300
  memory_size      = 512

  dynamic "vpc_config" {
    for_each = length(var.vpc_subnet_ids) > 0 ? [1] : []
    content {
      subnet_ids         = var.vpc_subnet_ids
      security_group_ids = var.lambda_security_group_id
    }
  }

  tags = var.tags
}

Lambdaの実行自体は手動コマンドで実行してもいいし、null_resourceでコレクションの作成にフックしてaws cliを実行してもいい。

Discussion