opensearch (terraform)

Introduction

Tip: module 'opensearch'.

Terraform resource for managing an AWS OpenSearch.


Resources (AWS OpenSearch Serverless): 

aws_opensearchserverless_access_policy           awscc_opensearchserverless_access_policy

aws_opensearchserverless_collection              awscc_opensearchserverless_collection

aws_opensearchserverless_security_config         awscc_opensearchserverless_security_config

aws_opensearchserverless_security_policy         awscc_opensearchserverless_security_policy

aws_opensearchserverless_vpc_endpoint            awscc_opensearchserverless_vpc_endpoint


Reference

Introduction to OpenSearch

https://opensearch.org/docs/latest/about/

Querying your Amazon OpenSearch Service data with SQL

https://docs.aws.amazon.com/opensearch-service/latest/developerguide/sql-support.html


Sample: Collection of type SEARCH

This is experimental, not yet ready for usage


modules/opensearch/variables.tf

variable "creatable" {

  description = "If opensearch will be created"

  nullable    = false

  type        = bool

}


variable "vpc_id" {}

variable "subnets_ids" {}


variable "vpc_endpoint_sgs_ids" {

  description = "SGs Ids that will use the OSS VPC endpoint (in addition to the OSS SG itself)"

  nullable    = false

}

modules/opensearch/outputs.tf

output "collection_endpoint" {

  value = try(aws_opensearchserverless_collection.example[0].collection_endpoint, null)

}


output "dashboard_endpoint" {

  value = try(aws_opensearchserverless_collection.example[0].dashboard_endpoint, null)

}

modules/opensearch/opensearch.tf

#

# EXPERIMENTAL, NOT YET READY FOR USAGE

#


locals {

  env                = data.aws_default_tags.common.tags.env

  ci                 = data.aws_default_tags.common.tags.ci

  collection_keyword = "sample7"

  collection_name    = "${local.env}-${local.ci}-${local.collection_keyword}"

}


# Get the default tags from the provider

data "aws_default_tags" "common" {}



# An aws_opensearchserverless_collection cannot be created without having an applicable encryption security policy. Use the depends_on meta-argument to define this dependency.

resource "aws_opensearchserverless_security_policy" "example_encryption" {

  count = var.creatable ? 1 : 0


  # Args required

  name = "${local.collection_name}-security"

  policy = jsonencode({

    Rules = [

      {

        Resource = [

          "collection/${local.collection_name}" #Match the collection name

        ],

        ResourceType = "collection"

      }

    ],

    AWSOwnedKey = true

  })

  type = "encryption"

  # Args optional

  description = "encryption policy for ${local.collection_name}"

}


# Collection creation

resource "aws_opensearchserverless_collection" "example" {

  count      = var.creatable ? 1 : 0

  depends_on = [aws_opensearchserverless_security_policy.example_encryption]


  # Args required

  name = local.collection_name

  # Args optional

  description = "Description of the collection"

  tags        = {}

  type        = "SEARCH" # One of SEARCH, TIMESERIES, or VECTORSEARCH

}




# Creates a security group to be associate w/ the opensearch serverless vpc endpoint

resource "aws_security_group" "security_group" {

  count = var.creatable ? 1 : 0


  description = "OpenSearch Serverless" #Maps to the AWS GroupDescription attribute, for which there is no Update API

  name_prefix = "${local.collection_name}-opensearch-"

  tags = {

    Name = "${local.collection_name} OSS collection"

  }

  vpc_id = var.vpc_id

}



# Allows all outbound traffic (IPv4)

resource "aws_vpc_security_group_egress_rule" "sg_egress_ipv4" {

  count = var.creatable ? 1 : 0


  cidr_ipv4 = "0.0.0.0/0"

  #cidr_ipv6 = "::/0"

  description = "Allow all outbound traffic (IPv4)"

  #from_port # Required unless ip_protocol is set to -1 or icmpv6

  ip_protocol       = "-1"

  security_group_id = aws_security_group.security_group[0].id

  #to_port # Required unless ip_protocol is set to -1 or icmpv6

}


# Allows all outbound traffic (IPv6)

resource "aws_vpc_security_group_egress_rule" "sg_egress_ipv6" {

  count = var.creatable ? 1 : 0


  #cidr_ipv4 = "0.0.0.0/0"

  cidr_ipv6   = "::/0"

  description = "Allow all outbound traffic (IPv6)"

  #from_port # Required unless ip_protocol is set to -1 or icmpv6

  ip_protocol       = "-1"

  security_group_id = aws_security_group.security_group[0].id

  #to_port # Required unless ip_protocol is set to -1 or icmpv6

}


# Allows inbound traffic from within security group

resource "aws_vpc_security_group_ingress_rule" "sg_ingress_within" {

  count = var.creatable ? 1 : 0


  #cidr_ipv4 #(Optional) The source IPv4 CIDR range

  #cidr_ipv6 #(Optional) The source IPv6 CIDR range

  description = "Allows inbound traffic from within security group"

  #from_port # Required unless ip_protocol is set to -1 or icmpv6

  ip_protocol                  = "-1"

  referenced_security_group_id = aws_security_group.security_group[0].id #(Optional) The source security group that is referenced in the rule

  security_group_id            = aws_security_group.security_group[0].id

  #to_port # Required unless ip_protocol is set to -1 or icmpv6

}


# Creates VPC endpoint to allow a private connection between your VPC and OpenSearch Serverless (seems that only one per vpc allowed)

resource "aws_opensearchserverless_vpc_endpoint" "example_vpc_endpoint" {

  count = var.creatable ? 1 : 0


  name       = "${local.env}-${var.vpc_id}-ep"

  vpc_id     = var.vpc_id

  subnet_ids = var.subnets_ids

  # Make sure that the security group rules allow the resources that will use the VPC endpoint to communicate with OpenSearch Serverless to communicate with the endpoint network interface

  security_group_ids = setunion([aws_security_group.security_group[0].id], var.vpc_endpoint_sgs_ids)

}


# Creates a network security policy

resource "aws_opensearchserverless_security_policy" "example_network" {

  count = var.creatable ? 1 : 0


  name        = "${local.collection_name}-network"

  type        = "network"

  description = "public access for dashboard, VPC access for collection endpoint"

  policy = jsonencode([

    {

      Description = "OSS VPC access for collection endpoint",

      Rules = [

        {

          ResourceType = "collection",

          Resource = [

            "collection/${local.collection_name}"

          ]

        }

      ],

      AllowFromPublic = false,

      SourceVPCEs = [

        aws_opensearchserverless_vpc_endpoint.example_vpc_endpoint[0].id

      ]

    },

    {

      Description = "Public access for dashboards",

      Rules = [

        {

          ResourceType = "dashboard"

          Resource = [

            "collection/${local.collection_name}"

          ]

        }

      ],

      AllowFromPublic = true

    }

  ])

}




# Gets access to the effective Account ID, User ID, and ARN in which Terraform is authorized

data "aws_caller_identity" "current" {}


# Creates a data access policy

resource "aws_opensearchserverless_access_policy" "example_access" {

  count = var.creatable ? 1 : 0


  name = "${local.collection_name}-data"

  policy = jsonencode([

    {

      Rules = [

        {

          ResourceType = "index",

          Resource = [

            "index/${local.collection_name}/*"

          ],

          Permission = [

            "aoss:*"

          ]

        },

        {

          ResourceType = "collection",

          Resource = [

            "collection/${local.collection_name}"

          ],

          Permission = [

            "aoss:*"

          ]

        }

      ],

      Principal = [

        data.aws_caller_identity.current.arn

      ]

    }

  ])

  type = "data"

  # Args optional

  description = "Allow index and collection access for ${local.collection_name}"

}