├── examples ├── collection │ ├── variables.tf │ ├── versions.tf │ ├── main.tf │ ├── outputs.tf │ └── README.md ├── complete │ ├── variables.tf │ ├── versions.tf │ ├── outputs.tf │ ├── README.md │ └── main.tf └── README.md ├── modules ├── README.md └── collection │ ├── versions.tf │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── README.md ├── wrappers ├── outputs.tf ├── collection │ ├── outputs.tf │ ├── versions.tf │ ├── variables.tf │ ├── README.md │ └── main.tf ├── versions.tf ├── variables.tf ├── README.md └── main.tf ├── versions.tf ├── .editorconfig ├── .gitignore ├── .github └── workflows │ ├── lock.yml │ ├── release.yml │ ├── stale-actions.yaml │ ├── pr-title.yml │ └── pre-commit.yml ├── .releaserc.json ├── .pre-commit-config.yaml ├── outputs.tf ├── CHANGELOG.md ├── LICENSE ├── variables.tf ├── README.md └── main.tf /examples/collection/variables.tf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/complete/variables.tf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /modules/README.md: -------------------------------------------------------------------------------- 1 | # AWS OpenSearch Terraform sub-module(s) 2 | -------------------------------------------------------------------------------- /wrappers/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /wrappers/collection/outputs.tf: -------------------------------------------------------------------------------- 1 | output "wrapper" { 2 | description = "Map of outputs of a wrapper." 3 | value = module.wrapper 4 | # sensitive = false # No sensitive module output found 5 | } 6 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5.7" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.20" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5.7" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.20" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/collection/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5.7" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.20" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/complete/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5.7" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.20" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/collection/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5.7" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.20" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/collection/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.5.7" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 6.20" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /wrappers/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /wrappers/collection/variables.tf: -------------------------------------------------------------------------------- 1 | variable "defaults" { 2 | description = "Map of default values which will be used for each item." 3 | type = any 4 | default = {} 5 | } 6 | 7 | variable "items" { 8 | description = "Maps of items to create a wrapper from. Values are passed through to the module." 9 | type = any 10 | default = {} 11 | } 12 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Please note - the examples provided serve two primary means: 4 | 5 | 1. Show users working examples of the various ways in which the module can be configured and features supported 6 | 2. A means of testing/validating module changes 7 | 8 | Please do not mistake the examples provided as "best practices". It is up to users to consult the AWS service documentation for best practices, usage recommendations, etc. 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | # Uses editorconfig to maintain consistent coding styles 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | indent_style = space 13 | insert_final_newline = true 14 | max_line_length = 80 15 | trim_trailing_whitespace = true 16 | 17 | [*.{tf,tfvars}] 18 | indent_size = 2 19 | indent_style = space 20 | 21 | [*.md] 22 | max_line_length = 0 23 | trim_trailing_whitespace = false 24 | 25 | [Makefile] 26 | tab_width = 2 27 | indent_style = tab 28 | 29 | [COMMIT_EDITMSG] 30 | max_line_length = 0 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # Terraform lockfile 5 | .terraform.lock.hcl 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | 11 | # Crash log files 12 | crash.log 13 | 14 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 15 | # password, private keys, and other secrets. These should not be part of version 16 | # control as they are data points which are potentially sensitive and subject 17 | # to change depending on the environment. 18 | *.tfvars 19 | 20 | # Ignore override files as they are usually used to override resources locally and so 21 | # are not checked in 22 | override.tf 23 | override.tf.json 24 | *_override.tf 25 | *_override.tf.json 26 | 27 | # Ignore CLI configuration files 28 | .terraformrc 29 | terraform.rc 30 | 31 | # Lambda build artifacts 32 | builds/ 33 | __pycache__/ 34 | *.zip 35 | .tox 36 | 37 | # Local editors/macos files 38 | .DS_Store 39 | .idea 40 | -------------------------------------------------------------------------------- /.github/workflows/lock.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock Threads' 2 | 3 | on: 4 | schedule: 5 | - cron: '50 1 * * *' 6 | 7 | jobs: 8 | lock: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: dessant/lock-threads@v5 12 | with: 13 | github-token: ${{ secrets.GITHUB_TOKEN }} 14 | issue-comment: > 15 | I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. 16 | If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. 17 | issue-inactive-days: '30' 18 | pr-comment: > 19 | I'm going to lock this pull request because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues. 20 | If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. 21 | pr-inactive-days: '30' 22 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "master" 5 | ], 6 | "ci": false, 7 | "plugins": [ 8 | [ 9 | "@semantic-release/commit-analyzer", 10 | { 11 | "preset": "conventionalcommits" 12 | } 13 | ], 14 | [ 15 | "@semantic-release/release-notes-generator", 16 | { 17 | "preset": "conventionalcommits" 18 | } 19 | ], 20 | [ 21 | "@semantic-release/github", 22 | { 23 | "successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:", 24 | "labels": false, 25 | "releasedLabels": false 26 | } 27 | ], 28 | [ 29 | "@semantic-release/changelog", 30 | { 31 | "changelogFile": "CHANGELOG.md", 32 | "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file." 33 | } 34 | ], 35 | [ 36 | "@semantic-release/git", 37 | { 38 | "assets": [ 39 | "CHANGELOG.md" 40 | ], 41 | "message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 42 | } 43 | ] 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | paths: 10 | - '**/*.tpl' 11 | - '**/*.py' 12 | - '**/*.tf' 13 | - '.github/workflows/release.yml' 14 | 15 | jobs: 16 | release: 17 | name: Release 18 | runs-on: ubuntu-latest 19 | # Skip running release workflow on forks 20 | if: github.repository_owner == 'terraform-aws-modules' 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v5 24 | with: 25 | persist-credentials: false 26 | fetch-depth: 0 27 | 28 | - name: Set correct Node.js version 29 | uses: actions/setup-node@v6 30 | with: 31 | node-version: 24 32 | 33 | - name: Install dependencies 34 | run: | 35 | npm install \ 36 | @semantic-release/changelog@6.0.3 \ 37 | @semantic-release/git@10.0.1 \ 38 | conventional-changelog-conventionalcommits@9.1.0 39 | 40 | - name: Release 41 | uses: cycjimmy/semantic-release-action@v5 42 | with: 43 | semantic_version: 25.0.0 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }} 46 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/antonbabenko/pre-commit-terraform 3 | rev: v1.103.0 4 | hooks: 5 | - id: terraform_fmt 6 | - id: terraform_wrapper_module_for_each 7 | - id: terraform_docs 8 | args: 9 | - '--args=--lockfile=false' 10 | - id: terraform_tflint 11 | args: 12 | - '--args=--only=terraform_deprecated_interpolation' 13 | - '--args=--only=terraform_deprecated_index' 14 | - '--args=--only=terraform_unused_declarations' 15 | - '--args=--only=terraform_comment_syntax' 16 | - '--args=--only=terraform_documented_outputs' 17 | - '--args=--only=terraform_documented_variables' 18 | - '--args=--only=terraform_typed_variables' 19 | - '--args=--only=terraform_module_pinned_source' 20 | - '--args=--only=terraform_naming_convention' 21 | - '--args=--only=terraform_required_version' 22 | - '--args=--only=terraform_required_providers' 23 | - '--args=--only=terraform_standard_module_structure' 24 | - '--args=--only=terraform_workspace_remote' 25 | - id: terraform_validate 26 | - repo: https://github.com/pre-commit/pre-commit-hooks 27 | rev: v6.0.0 28 | hooks: 29 | - id: check-merge-conflict 30 | - id: end-of-file-fixer 31 | - id: trailing-whitespace 32 | -------------------------------------------------------------------------------- /.github/workflows/stale-actions.yaml: -------------------------------------------------------------------------------- 1 | name: 'Mark or close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v10 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | # Staling issues and PR's 14 | days-before-stale: 30 15 | stale-issue-label: stale 16 | stale-pr-label: stale 17 | stale-issue-message: | 18 | This issue has been automatically marked as stale because it has been open 30 days 19 | with no activity. Remove stale label or comment or this issue will be closed in 10 days 20 | stale-pr-message: | 21 | This PR has been automatically marked as stale because it has been open 30 days 22 | with no activity. Remove stale label or comment or this PR will be closed in 10 days 23 | # Not stale if have this labels or part of milestone 24 | exempt-issue-labels: bug,wip,on-hold 25 | exempt-pr-labels: bug,wip,on-hold 26 | exempt-all-milestones: true 27 | # Close issue operations 28 | # Label will be automatically removed if the issues are no longer closed nor locked. 29 | days-before-close: 10 30 | delete-branch: true 31 | close-issue-message: This issue was automatically closed because of stale in 10 days 32 | close-pr-message: This PR was automatically closed because of stale in 10 days 33 | -------------------------------------------------------------------------------- /.github/workflows/pr-title.yml: -------------------------------------------------------------------------------- 1 | name: 'Validate PR title' 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Please look up the latest version from 16 | # https://github.com/amannn/action-semantic-pull-request/releases 17 | - uses: amannn/action-semantic-pull-request@v6.1.1 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | # Configure which types are allowed. 22 | # Default: https://github.com/commitizen/conventional-commit-types 23 | types: | 24 | fix 25 | feat 26 | docs 27 | ci 28 | chore 29 | # Configure that a scope must always be provided. 30 | requireScope: false 31 | # Configure additional validation for the subject based on a regex. 32 | # This example ensures the subject starts with an uppercase character. 33 | subjectPattern: ^[A-Z].+$ 34 | # If `subjectPattern` is configured, you can use this property to override 35 | # the default error message that is shown when the pattern doesn't match. 36 | # The variables `subject` and `title` can be used within the message. 37 | subjectPatternError: | 38 | The subject "{subject}" found in the pull request title "{title}" 39 | didn't match the configured pattern. Please ensure that the subject 40 | starts with an uppercase character. 41 | # For work-in-progress PRs you can typically use draft pull requests 42 | # from Github. However, private repositories on the free plan don't have 43 | # this option and therefore this action allows you to opt-in to using the 44 | # special "[WIP]" prefix to indicate this state. This will avoid the 45 | # validation of the PR title and the pull request checks remain pending. 46 | # Note that a second check will be reported if this is enabled. 47 | wip: true 48 | # When using "Squash and merge" on a PR with only one commit, GitHub 49 | # will suggest using that commit message instead of the PR title for the 50 | # merge commit, and it's easy to commit this by mistake. Enable this option 51 | # to also validate the commit message for one commit PRs. 52 | validateSingleCommit: false 53 | -------------------------------------------------------------------------------- /examples/collection/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | data "aws_availability_zones" "available" {} 6 | 7 | locals { 8 | region = "eu-west-1" 9 | name = "ex-${basename(path.cwd)}" 10 | 11 | vpc_cidr = "10.0.0.0/16" 12 | azs = slice(data.aws_availability_zones.available.names, 0, 3) 13 | 14 | tags = { 15 | Name = local.name 16 | Example = local.name 17 | Repository = "https://github.com/terraform-aws-modules/terraform-aws-opensearch" 18 | } 19 | } 20 | 21 | ################################################################################ 22 | # OpenSearch Module 23 | ################################################################################ 24 | 25 | module "opensearch_collection_public" { 26 | source = "../../modules/collection" 27 | 28 | name = "${local.name}-public" 29 | description = "Example public OpenSearch Serverless collection" 30 | type = "SEARCH" 31 | standby_replicas = "DISABLED" 32 | 33 | create_access_policy = true 34 | create_network_policy = true 35 | 36 | tags = local.tags 37 | } 38 | 39 | module "opensearch_collection_private" { 40 | source = "../../modules/collection" 41 | 42 | name = "${local.name}-private" 43 | description = "Example private OpenSearch Serverless collection" 44 | type = "SEARCH" 45 | 46 | create_access_policy = true 47 | create_network_policy = true 48 | network_policy = { 49 | AllowFromPublic = false 50 | SourceVPCEs = [ 51 | aws_opensearchserverless_vpc_endpoint.example.id 52 | ] 53 | } 54 | 55 | tags = local.tags 56 | } 57 | 58 | module "opensearch_collection_disabled" { 59 | source = "../../modules/collection" 60 | 61 | create = false 62 | } 63 | 64 | ################################################################################ 65 | # Supporting Resources 66 | ################################################################################ 67 | 68 | module "vpc" { 69 | source = "terraform-aws-modules/vpc/aws" 70 | version = "~> 6.0" 71 | 72 | name = local.name 73 | cidr = local.vpc_cidr 74 | 75 | azs = local.azs 76 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)] 77 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 10)] 78 | 79 | tags = local.tags 80 | } 81 | 82 | resource "aws_opensearchserverless_vpc_endpoint" "example" { 83 | name = local.name 84 | subnet_ids = module.vpc.private_subnets 85 | vpc_id = module.vpc.vpc_id 86 | } 87 | -------------------------------------------------------------------------------- /wrappers/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for the root module 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/opensearch/aws//wrappers" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-opensearch.git//wrappers?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/opensearch/aws//wrappers" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /wrappers/collection/README.md: -------------------------------------------------------------------------------- 1 | # Wrapper for module: `modules/collection` 2 | 3 | The configuration in this directory contains an implementation of a single module wrapper pattern, which allows managing several copies of a module in places where using the native Terraform 0.13+ `for_each` feature is not feasible (e.g., with Terragrunt). 4 | 5 | You may want to use a single Terragrunt configuration file to manage multiple resources without duplicating `terragrunt.hcl` files for each copy of the same module. 6 | 7 | This wrapper does not implement any extra functionality. 8 | 9 | ## Usage with Terragrunt 10 | 11 | `terragrunt.hcl`: 12 | 13 | ```hcl 14 | terraform { 15 | source = "tfr:///terraform-aws-modules/opensearch/aws//wrappers/collection" 16 | # Alternative source: 17 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-opensearch.git//wrappers/collection?ref=master" 18 | } 19 | 20 | inputs = { 21 | defaults = { # Default values 22 | create = true 23 | tags = { 24 | Terraform = "true" 25 | Environment = "dev" 26 | } 27 | } 28 | 29 | items = { 30 | my-item = { 31 | # omitted... can be any argument supported by the module 32 | } 33 | my-second-item = { 34 | # omitted... can be any argument supported by the module 35 | } 36 | # omitted... 37 | } 38 | } 39 | ``` 40 | 41 | ## Usage with Terraform 42 | 43 | ```hcl 44 | module "wrapper" { 45 | source = "terraform-aws-modules/opensearch/aws//wrappers/collection" 46 | 47 | defaults = { # Default values 48 | create = true 49 | tags = { 50 | Terraform = "true" 51 | Environment = "dev" 52 | } 53 | } 54 | 55 | items = { 56 | my-item = { 57 | # omitted... can be any argument supported by the module 58 | } 59 | my-second-item = { 60 | # omitted... can be any argument supported by the module 61 | } 62 | # omitted... 63 | } 64 | } 65 | ``` 66 | 67 | ## Example: Manage multiple S3 buckets in one Terragrunt layer 68 | 69 | `eu-west-1/s3-buckets/terragrunt.hcl`: 70 | 71 | ```hcl 72 | terraform { 73 | source = "tfr:///terraform-aws-modules/s3-bucket/aws//wrappers" 74 | # Alternative source: 75 | # source = "git::git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git//wrappers?ref=master" 76 | } 77 | 78 | inputs = { 79 | defaults = { 80 | force_destroy = true 81 | 82 | attach_elb_log_delivery_policy = true 83 | attach_lb_log_delivery_policy = true 84 | attach_deny_insecure_transport_policy = true 85 | attach_require_latest_tls_policy = true 86 | } 87 | 88 | items = { 89 | bucket1 = { 90 | bucket = "my-random-bucket-1" 91 | } 92 | bucket2 = { 93 | bucket = "my-random-bucket-2" 94 | tags = { 95 | Secure = "probably" 96 | } 97 | } 98 | } 99 | } 100 | ``` 101 | -------------------------------------------------------------------------------- /examples/complete/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Domain 3 | ################################################################################ 4 | 5 | output "domain_arn" { 6 | description = "The Amazon Resource Name (ARN) of the domain" 7 | value = module.opensearch.domain_arn 8 | } 9 | 10 | output "domain_id" { 11 | description = "The unique identifier for the domain" 12 | value = module.opensearch.domain_id 13 | } 14 | 15 | output "domain_name" { 16 | description = "The name of the domain" 17 | value = module.opensearch.domain_name 18 | } 19 | 20 | output "domain_endpoint" { 21 | description = "Domain-specific endpoint used to submit index, search, and data upload requests" 22 | value = module.opensearch.domain_endpoint 23 | } 24 | 25 | output "domain_dashboard_endpoint" { 26 | description = "Domain-specific endpoint for Dashboard without https scheme" 27 | value = module.opensearch.domain_dashboard_endpoint 28 | } 29 | 30 | ################################################################################ 31 | # Package Association(s) 32 | ################################################################################ 33 | 34 | output "package_associations" { 35 | description = "Map of package associations created and their attributes" 36 | value = module.opensearch.package_associations 37 | } 38 | 39 | ################################################################################ 40 | # VPC Endpoint(s) 41 | ################################################################################ 42 | 43 | output "vpc_endpoints" { 44 | description = "Map of VPC endpoints created and their attributes" 45 | value = module.opensearch.vpc_endpoints 46 | } 47 | 48 | ################################################################################ 49 | # Outbound Connections 50 | ################################################################################ 51 | 52 | output "outbound_connections" { 53 | description = "Map of outbound connections created and their attributes" 54 | value = module.opensearch.outbound_connections 55 | } 56 | 57 | ################################################################################ 58 | # CloudWatch Log Groups 59 | ################################################################################ 60 | 61 | output "cloudwatch_logs" { 62 | description = "Map of CloudWatch log groups created and their attributes" 63 | value = module.opensearch.cloudwatch_logs 64 | } 65 | 66 | ################################################################################ 67 | # Security Group 68 | ################################################################################ 69 | 70 | output "security_group_arn" { 71 | description = "Amazon Resource Name (ARN) of the security group" 72 | value = module.opensearch.security_group_arn 73 | } 74 | 75 | output "security_group_id" { 76 | description = "ID of the security group" 77 | value = module.opensearch.security_group_id 78 | } 79 | -------------------------------------------------------------------------------- /wrappers/collection/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../../modules/collection" 3 | 4 | for_each = var.items 5 | 6 | access_policy = try(each.value.access_policy, var.defaults.access_policy, {}) 7 | access_policy_collection_permissions = try(each.value.access_policy_collection_permissions, var.defaults.access_policy_collection_permissions, ["aoss:*"]) 8 | access_policy_description = try(each.value.access_policy_description, var.defaults.access_policy_description, null) 9 | access_policy_index_permissions = try(each.value.access_policy_index_permissions, var.defaults.access_policy_index_permissions, ["aoss:*"]) 10 | access_policy_name = try(each.value.access_policy_name, var.defaults.access_policy_name, null) 11 | access_policy_principals = try(each.value.access_policy_principals, var.defaults.access_policy_principals, []) 12 | create = try(each.value.create, var.defaults.create, true) 13 | create_access_policy = try(each.value.create_access_policy, var.defaults.create_access_policy, false) 14 | create_encryption_policy = try(each.value.create_encryption_policy, var.defaults.create_encryption_policy, true) 15 | create_lifecycle_policy = try(each.value.create_lifecycle_policy, var.defaults.create_lifecycle_policy, false) 16 | create_network_policy = try(each.value.create_network_policy, var.defaults.create_network_policy, false) 17 | description = try(each.value.description, var.defaults.description, null) 18 | encryption_policy = try(each.value.encryption_policy, var.defaults.encryption_policy, {}) 19 | encryption_policy_description = try(each.value.encryption_policy_description, var.defaults.encryption_policy_description, null) 20 | encryption_policy_name = try(each.value.encryption_policy_name, var.defaults.encryption_policy_name, null) 21 | lifecycle_policy_description = try(each.value.lifecycle_policy_description, var.defaults.lifecycle_policy_description, null) 22 | lifecycle_policy_min_index_retention = try(each.value.lifecycle_policy_min_index_retention, var.defaults.lifecycle_policy_min_index_retention, null) 23 | lifecycle_policy_name = try(each.value.lifecycle_policy_name, var.defaults.lifecycle_policy_name, null) 24 | lifecycle_policy_no_min_index_retention = try(each.value.lifecycle_policy_no_min_index_retention, var.defaults.lifecycle_policy_no_min_index_retention, null) 25 | name = try(each.value.name, var.defaults.name, "") 26 | network_policy = try(each.value.network_policy, var.defaults.network_policy, {}) 27 | network_policy_description = try(each.value.network_policy_description, var.defaults.network_policy_description, null) 28 | network_policy_name = try(each.value.network_policy_name, var.defaults.network_policy_name, null) 29 | region = try(each.value.region, var.defaults.region, null) 30 | standby_replicas = try(each.value.standby_replicas, var.defaults.standby_replicas, null) 31 | tags = try(each.value.tags, var.defaults.tags, {}) 32 | timeouts = try(each.value.timeouts, var.defaults.timeouts, {}) 33 | type = try(each.value.type, var.defaults.type, null) 34 | } 35 | -------------------------------------------------------------------------------- /examples/complete/README.md: -------------------------------------------------------------------------------- 1 | # Complete AWS OpenSearch Example 2 | 3 | Configuration in this directory creates an AWS OpenSearch domain and resources 4 | 5 | ## Usage 6 | 7 | Ensure you have a service-linked role for OpenSearch before deploying the example. You can use the following command to create one: 8 | 9 | ```bash 10 | aws iam create-service-linked-role --aws-service-name es.amazonaws.com 11 | ``` 12 | 13 | To run this example you need to execute: 14 | 15 | ```bash 16 | $ terraform init 17 | $ terraform plan 18 | $ terraform apply 19 | ``` 20 | 21 | Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. 22 | 23 | 24 | ## Requirements 25 | 26 | | Name | Version | 27 | |------|---------| 28 | | [terraform](#requirement\_terraform) | >= 1.5.7 | 29 | | [aws](#requirement\_aws) | >= 6.20 | 30 | 31 | ## Providers 32 | 33 | | Name | Version | 34 | |------|---------| 35 | | [aws](#provider\_aws) | >= 6.20 | 36 | 37 | ## Modules 38 | 39 | | Name | Source | Version | 40 | |------|--------|---------| 41 | | [opensearch](#module\_opensearch) | ../.. | n/a | 42 | | [opensearch\_disabled](#module\_opensearch\_disabled) | ../.. | n/a | 43 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | 44 | 45 | ## Resources 46 | 47 | | Name | Type | 48 | |------|------| 49 | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 50 | 51 | ## Inputs 52 | 53 | No inputs. 54 | 55 | ## Outputs 56 | 57 | | Name | Description | 58 | |------|-------------| 59 | | [cloudwatch\_logs](#output\_cloudwatch\_logs) | Map of CloudWatch log groups created and their attributes | 60 | | [domain\_arn](#output\_domain\_arn) | The Amazon Resource Name (ARN) of the domain | 61 | | [domain\_dashboard\_endpoint](#output\_domain\_dashboard\_endpoint) | Domain-specific endpoint for Dashboard without https scheme | 62 | | [domain\_endpoint](#output\_domain\_endpoint) | Domain-specific endpoint used to submit index, search, and data upload requests | 63 | | [domain\_id](#output\_domain\_id) | The unique identifier for the domain | 64 | | [domain\_name](#output\_domain\_name) | The name of the domain | 65 | | [outbound\_connections](#output\_outbound\_connections) | Map of outbound connections created and their attributes | 66 | | [package\_associations](#output\_package\_associations) | Map of package associations created and their attributes | 67 | | [security\_group\_arn](#output\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group | 68 | | [security\_group\_id](#output\_security\_group\_id) | ID of the security group | 69 | | [vpc\_endpoints](#output\_vpc\_endpoints) | Map of VPC endpoints created and their attributes | 70 | 71 | 72 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-opensearch/blob/master/LICENSE). 73 | -------------------------------------------------------------------------------- /modules/collection/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Collection 3 | ################################################################################ 4 | 5 | output "arn" { 6 | description = "Amazon Resource Name (ARN) of the collection" 7 | value = try(aws_opensearchserverless_collection.this[0].arn, null) 8 | } 9 | 10 | output "endpoint" { 11 | description = "Collection-specific endpoint used to submit index, search, and data upload requests to an OpenSearch Serverless collection" 12 | value = try(aws_opensearchserverless_collection.this[0].collection_endpoint, null) 13 | } 14 | 15 | output "dashboard_endpoint" { 16 | description = "Collection-specific endpoint used to access OpenSearch Dashboards" 17 | value = try(aws_opensearchserverless_collection.this[0].dashboard_endpoint, null) 18 | } 19 | 20 | output "kms_key_arn" { 21 | description = "The ARN of the Amazon Web Services KMS key used to encrypt the collection" 22 | value = try(aws_opensearchserverless_collection.this[0].kms_key_arn, null) 23 | } 24 | 25 | output "id" { 26 | description = "Unique identifier for the collection" 27 | value = try(aws_opensearchserverless_collection.this[0].id, null) 28 | } 29 | 30 | output "name" { 31 | description = "Name of the collection" 32 | value = try(aws_opensearchserverless_collection.this[0].name, null) 33 | } 34 | 35 | ################################################################################ 36 | # Encryption Policy 37 | ################################################################################ 38 | 39 | output "encryption_policy_version" { 40 | description = "The version of the encryption policy" 41 | value = try(aws_opensearchserverless_security_policy.encryption[0].policy_version, null) 42 | } 43 | 44 | output "encryption_policy" { 45 | description = "The JSON policy document of the encryption policy" 46 | value = try(aws_opensearchserverless_security_policy.encryption[0].policy, null) 47 | } 48 | 49 | ################################################################################ 50 | # Network Policy 51 | ################################################################################ 52 | 53 | output "network_policy_version" { 54 | description = "The version of the network policy" 55 | value = try(aws_opensearchserverless_security_policy.network[0].policy_version, null) 56 | } 57 | 58 | output "network_policy" { 59 | description = "The JSON policy document of the network policy" 60 | value = try(aws_opensearchserverless_security_policy.network[0].policy, null) 61 | } 62 | 63 | ################################################################################ 64 | # Access Policy 65 | ################################################################################ 66 | 67 | output "access_policy_version" { 68 | description = "The version of the access policy" 69 | value = try(aws_opensearchserverless_access_policy.this[0].policy_version, null) 70 | } 71 | 72 | output "access_policy" { 73 | description = "The JSON policy document of the access policy" 74 | value = try(aws_opensearchserverless_access_policy.this[0].policy, null) 75 | } 76 | 77 | ################################################################################ 78 | # Lifecycle Policy 79 | ################################################################################ 80 | 81 | output "lifecycle_policy_version" { 82 | description = "The version of the lifecycle policy" 83 | value = try(aws_opensearchserverless_lifecycle_policy.this[0].policy_version, null) 84 | } 85 | 86 | output "lifecycle_policy" { 87 | description = "The JSON policy document of the lifecycle policy" 88 | value = try(aws_opensearchserverless_lifecycle_policy.this[0].policy, null) 89 | } 90 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Domain 3 | ################################################################################ 4 | 5 | output "domain_arn" { 6 | description = "The Amazon Resource Name (ARN) of the domain" 7 | value = try(aws_opensearch_domain.this[0].arn, null) 8 | } 9 | 10 | output "domain_id" { 11 | description = "The unique identifier for the domain" 12 | value = try(aws_opensearch_domain.this[0].domain_id, null) 13 | } 14 | 15 | output "domain_name" { 16 | description = "The name of the domain" 17 | value = try(aws_opensearch_domain.this[0].domain_name, null) 18 | } 19 | 20 | output "domain_endpoint" { 21 | description = "Domain-specific endpoint used to submit index, search, and data upload requests" 22 | value = try(aws_opensearch_domain.this[0].endpoint, null) 23 | } 24 | 25 | output "domain_endpoint_v2" { 26 | description = "V2 domain endpoint that works with both IPv4 and IPv6 addresses, used to submit index, search, and data upload requests" 27 | value = try(aws_opensearch_domain.this[0].endpoint_v2, null) 28 | } 29 | 30 | output "domain_dashboard_endpoint" { 31 | description = "Domain-specific endpoint for Dashboard without https scheme" 32 | value = try(aws_opensearch_domain.this[0].dashboard_endpoint, null) 33 | } 34 | 35 | output "domain_dashboard_endpoint_v2" { 36 | description = "V2 domain endpoint for Dashboard that works with both IPv4 and IPv6 addresses, without https scheme" 37 | value = try(aws_opensearch_domain.this[0].dashboard_endpoint_v2, null) 38 | } 39 | 40 | output "domain_endpoint_v2_hosted_zone_id" { 41 | description = "Dual stack hosted zone ID for the domain." 42 | value = try(aws_opensearch_domain.this[0].domain_endpoint_v2_hosted_zone_id, null) 43 | } 44 | 45 | ################################################################################ 46 | # Package Association(s) 47 | ################################################################################ 48 | 49 | output "package_associations" { 50 | description = "Map of package associations created and their attributes" 51 | value = aws_opensearch_package_association.this 52 | } 53 | 54 | ################################################################################ 55 | # VPC Endpoint(s) 56 | ################################################################################ 57 | 58 | output "vpc_endpoints" { 59 | description = "Map of VPC endpoints created and their attributes" 60 | value = aws_opensearch_vpc_endpoint.this 61 | } 62 | 63 | ################################################################################ 64 | # Outbound Connections 65 | ################################################################################ 66 | 67 | output "outbound_connections" { 68 | description = "Map of outbound connections created and their attributes" 69 | value = aws_opensearch_outbound_connection.this 70 | } 71 | 72 | ################################################################################ 73 | # CloudWatch Log Groups 74 | ################################################################################ 75 | 76 | output "cloudwatch_logs" { 77 | description = "Map of CloudWatch log groups created and their attributes" 78 | value = aws_cloudwatch_log_group.this 79 | } 80 | 81 | ################################################################################ 82 | # Security Group 83 | ################################################################################ 84 | 85 | output "security_group_arn" { 86 | description = "Amazon Resource Name (ARN) of the security group" 87 | value = try(aws_security_group.this[0].arn, null) 88 | } 89 | 90 | output "security_group_id" { 91 | description = "ID of the security group" 92 | value = try(aws_security_group.this[0].id, null) 93 | } 94 | -------------------------------------------------------------------------------- /examples/complete/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.region 3 | } 4 | 5 | data "aws_availability_zones" "available" {} 6 | 7 | locals { 8 | region = "eu-west-1" 9 | name = "ex-${basename(path.cwd)}" 10 | 11 | vpc_cidr = "10.0.0.0/16" 12 | azs = slice(data.aws_availability_zones.available.names, 0, 3) 13 | 14 | tags = { 15 | Name = local.name 16 | Example = local.name 17 | Repository = "https://github.com/terraform-aws-modules/terraform-aws-opensearch" 18 | } 19 | } 20 | 21 | ################################################################################ 22 | # OpenSearch Module 23 | ################################################################################ 24 | 25 | module "opensearch" { 26 | source = "../.." 27 | 28 | # Domain 29 | advanced_options = { 30 | "rest.action.multi.allow_explicit_index" = "true" 31 | } 32 | 33 | advanced_security_options = { 34 | enabled = false 35 | anonymous_auth_enabled = true 36 | internal_user_database_enabled = true 37 | 38 | master_user_options = { 39 | master_user_name = "example" 40 | master_user_password = "Barbarbarbar1!" 41 | } 42 | } 43 | 44 | auto_tune_options = { 45 | desired_state = "ENABLED" 46 | 47 | maintenance_schedule = [ 48 | { 49 | start_at = "2028-05-13T07:44:12Z" 50 | cron_expression_for_recurrence = "cron(0 0 ? * 1 *)" 51 | duration = { 52 | value = "2" 53 | unit = "HOURS" 54 | } 55 | } 56 | ] 57 | 58 | rollback_on_disable = "NO_ROLLBACK" 59 | } 60 | 61 | cluster_config = { 62 | instance_count = 3 63 | dedicated_master_enabled = true 64 | dedicated_master_type = "c6g.large.search" 65 | instance_type = "r6g.large.search" 66 | 67 | node_options = { 68 | coordinator = { 69 | node_config = { 70 | enabled = true 71 | count = 3 72 | type = "m6g.large.search" 73 | } 74 | } 75 | } 76 | 77 | zone_awareness_config = { 78 | availability_zone_count = 3 79 | } 80 | 81 | zone_awareness_enabled = true 82 | } 83 | 84 | domain_endpoint_options = { 85 | enforce_https = true 86 | tls_security_policy = "Policy-Min-TLS-1-2-2019-07" 87 | } 88 | 89 | domain_name = local.name 90 | 91 | ebs_options = { 92 | ebs_enabled = true 93 | iops = 3000 94 | throughput = 125 95 | volume_type = "gp3" 96 | volume_size = 20 97 | } 98 | 99 | encrypt_at_rest = { 100 | enabled = true 101 | } 102 | 103 | engine_version = "OpenSearch_2.13" 104 | 105 | log_publishing_options = [ 106 | { log_type = "INDEX_SLOW_LOGS" }, 107 | { log_type = "SEARCH_SLOW_LOGS" }, 108 | ] 109 | 110 | ip_address_type = "dualstack" 111 | 112 | node_to_node_encryption = { 113 | enabled = true 114 | } 115 | 116 | software_update_options = { 117 | auto_software_update_enabled = true 118 | } 119 | 120 | vpc_options = { 121 | subnet_ids = module.vpc.private_subnets 122 | } 123 | 124 | # VPC endpoint 125 | vpc_endpoints = { 126 | one = { 127 | subnet_ids = module.vpc.private_subnets 128 | } 129 | } 130 | 131 | # Security Group rule example 132 | security_group_rules = { 133 | ingress_443 = { 134 | type = "ingress" 135 | description = "HTTPS access from VPC" 136 | from_port = 443 137 | to_port = 443 138 | ip_protocol = "tcp" 139 | cidr_ipv4 = local.vpc_cidr 140 | } 141 | } 142 | 143 | # Access policy 144 | access_policy_statements = [ 145 | { 146 | effect = "Allow" 147 | 148 | principals = [{ 149 | type = "*" 150 | identifiers = ["*"] 151 | }] 152 | 153 | actions = ["es:*"] 154 | 155 | conditions = [{ 156 | test = "IpAddress" 157 | variable = "aws:SourceIp" 158 | values = ["127.0.0.1/32"] 159 | }] 160 | } 161 | ] 162 | 163 | tags = local.tags 164 | } 165 | 166 | module "opensearch_disabled" { 167 | source = "../.." 168 | 169 | create = false 170 | } 171 | 172 | ################################################################################ 173 | # Supporting Resources 174 | ################################################################################ 175 | 176 | module "vpc" { 177 | source = "terraform-aws-modules/vpc/aws" 178 | version = "~> 6.0" 179 | 180 | name = local.name 181 | cidr = local.vpc_cidr 182 | 183 | azs = local.azs 184 | public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k)] 185 | private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 10)] 186 | 187 | enable_ipv6 = true 188 | 189 | public_subnet_ipv6_prefixes = [0, 1, 2] 190 | private_subnet_ipv6_prefixes = [3, 4, 5] 191 | 192 | tags = local.tags 193 | } 194 | -------------------------------------------------------------------------------- /modules/collection/main.tf: -------------------------------------------------------------------------------- 1 | data "aws_caller_identity" "current" { 2 | count = var.create ? 1 : 0 3 | } 4 | 5 | locals { 6 | tags = merge(var.tags, { terraform-aws-modules = "opensearch" }) 7 | } 8 | 9 | ################################################################################ 10 | # Collection 11 | ################################################################################ 12 | 13 | resource "aws_opensearchserverless_collection" "this" { 14 | count = var.create ? 1 : 0 15 | 16 | description = var.description 17 | name = var.name 18 | region = var.region 19 | type = var.type 20 | standby_replicas = var.standby_replicas 21 | 22 | tags = local.tags 23 | 24 | timeouts { 25 | create = try(var.timeouts.delete, null) 26 | delete = try(var.timeouts.delete, null) 27 | } 28 | 29 | depends_on = [ 30 | aws_opensearchserverless_security_policy.encryption, 31 | aws_opensearchserverless_security_policy.network, 32 | aws_opensearchserverless_access_policy.this, 33 | ] 34 | } 35 | 36 | ################################################################################ 37 | # Encryption Policy 38 | ################################################################################ 39 | 40 | resource "aws_opensearchserverless_security_policy" "encryption" { 41 | count = var.create && var.create_encryption_policy ? 1 : 0 42 | 43 | description = coalesce(var.encryption_policy_description, "Encryption policy for ${var.name} collection") 44 | name = coalesce(var.encryption_policy_name, "${var.name}-encryption") 45 | policy = jsonencode(merge( 46 | { 47 | Rules = [ 48 | { 49 | Resource = ["collection/${var.name}"] 50 | ResourceType = "collection" 51 | } 52 | ] 53 | AWSOwnedKey = true 54 | }, 55 | var.encryption_policy 56 | )) 57 | 58 | region = var.region 59 | type = "encryption" 60 | } 61 | 62 | ################################################################################ 63 | # Network Policy 64 | ################################################################################ 65 | 66 | resource "aws_opensearchserverless_security_policy" "network" { 67 | count = var.create && var.create_network_policy ? 1 : 0 68 | 69 | description = coalesce(var.network_policy_description, "Network policy for ${var.name} collection") 70 | name = coalesce(var.network_policy_name, "${var.name}-network") 71 | policy = jsonencode([merge( 72 | { 73 | Rules = [ 74 | { 75 | Resource = ["collection/${var.name}"] 76 | ResourceType = "collection" 77 | }, 78 | { 79 | Resource = ["collection/${var.name}"] 80 | ResourceType = "dashboard" 81 | } 82 | ] 83 | AllowFromPublic = true 84 | }, 85 | var.network_policy 86 | )]) 87 | 88 | region = var.region 89 | type = "network" 90 | } 91 | 92 | ################################################################################ 93 | # Access Policy 94 | ################################################################################ 95 | 96 | resource "aws_opensearchserverless_access_policy" "this" { 97 | count = var.create && var.create_access_policy ? 1 : 0 98 | 99 | description = coalesce(var.access_policy_description, "Access policy for ${var.name} collection") 100 | name = coalesce(var.access_policy_name, "${var.name}-access") 101 | policy = jsonencode([merge( 102 | { 103 | Rules = [ 104 | { 105 | ResourceType = "index", 106 | Resource = ["index/${var.name}/*"], 107 | Permission = var.access_policy_index_permissions, 108 | }, 109 | { 110 | ResourceType = "collection", 111 | Resource = ["collection/${var.name}"], 112 | Permission = var.access_policy_collection_permissions, 113 | } 114 | ], 115 | Principal = coalescelist(var.access_policy_principals, [data.aws_caller_identity.current[0].arn]) 116 | }, 117 | var.access_policy 118 | )]) 119 | 120 | region = var.region 121 | type = "data" 122 | } 123 | 124 | ################################################################################ 125 | # Lifecycle Policy 126 | ################################################################################ 127 | 128 | resource "aws_opensearchserverless_lifecycle_policy" "this" { 129 | count = var.create && var.create_lifecycle_policy ? 1 : 0 130 | 131 | description = coalesce(var.lifecycle_policy_description, "Lifecycle policy for ${var.name} collection") 132 | name = coalesce(var.lifecycle_policy_name, "${var.name}-lifecycle") 133 | policy = jsonencode( 134 | { 135 | Rules = [ 136 | { for k, v in { 137 | Resource = ["collection/${var.name}"] 138 | ResourceType = "collection" 139 | MinIndexRetention = var.lifecycle_policy_min_index_retention 140 | NoMinIndexRetention = var.lifecycle_policy_no_min_index_retention 141 | } 142 | : k => v if v != null } 143 | ] 144 | } 145 | ) 146 | 147 | region = var.region 148 | type = "retention" 149 | } 150 | -------------------------------------------------------------------------------- /wrappers/main.tf: -------------------------------------------------------------------------------- 1 | module "wrapper" { 2 | source = "../" 3 | 4 | for_each = var.items 5 | 6 | access_policies = try(each.value.access_policies, var.defaults.access_policies, null) 7 | access_policy_override_policy_documents = try(each.value.access_policy_override_policy_documents, var.defaults.access_policy_override_policy_documents, []) 8 | access_policy_source_policy_documents = try(each.value.access_policy_source_policy_documents, var.defaults.access_policy_source_policy_documents, []) 9 | access_policy_statements = try(each.value.access_policy_statements, var.defaults.access_policy_statements, {}) 10 | advanced_options = try(each.value.advanced_options, var.defaults.advanced_options, {}) 11 | advanced_security_options = try(each.value.advanced_security_options, var.defaults.advanced_security_options, { 12 | enabled = true 13 | anonymous_auth_enabled = false 14 | }) 15 | aiml_options = try(each.value.aiml_options, var.defaults.aiml_options, null) 16 | auto_tune_options = try(each.value.auto_tune_options, var.defaults.auto_tune_options, { 17 | desired_state = "ENABLED" 18 | rollback_on_disable = "NO_ROLLBACK" 19 | }) 20 | cloudwatch_log_group_class = try(each.value.cloudwatch_log_group_class, var.defaults.cloudwatch_log_group_class, null) 21 | cloudwatch_log_group_kms_key_id = try(each.value.cloudwatch_log_group_kms_key_id, var.defaults.cloudwatch_log_group_kms_key_id, null) 22 | cloudwatch_log_group_retention_in_days = try(each.value.cloudwatch_log_group_retention_in_days, var.defaults.cloudwatch_log_group_retention_in_days, 60) 23 | cloudwatch_log_group_skip_destroy = try(each.value.cloudwatch_log_group_skip_destroy, var.defaults.cloudwatch_log_group_skip_destroy, null) 24 | cloudwatch_log_resource_policy_name = try(each.value.cloudwatch_log_resource_policy_name, var.defaults.cloudwatch_log_resource_policy_name, null) 25 | cluster_config = try(each.value.cluster_config, var.defaults.cluster_config, { 26 | dedicated_master_enabled = true 27 | }) 28 | cognito_options = try(each.value.cognito_options, var.defaults.cognito_options, {}) 29 | create = try(each.value.create, var.defaults.create, true) 30 | create_access_policy = try(each.value.create_access_policy, var.defaults.create_access_policy, true) 31 | create_cloudwatch_log_groups = try(each.value.create_cloudwatch_log_groups, var.defaults.create_cloudwatch_log_groups, true) 32 | create_cloudwatch_log_resource_policy = try(each.value.create_cloudwatch_log_resource_policy, var.defaults.create_cloudwatch_log_resource_policy, true) 33 | create_saml_options = try(each.value.create_saml_options, var.defaults.create_saml_options, false) 34 | create_security_group = try(each.value.create_security_group, var.defaults.create_security_group, true) 35 | domain_endpoint_options = try(each.value.domain_endpoint_options, var.defaults.domain_endpoint_options, { 36 | enforce_https = true 37 | tls_security_policy = "Policy-Min-TLS-1-2-2019-07" 38 | }) 39 | domain_name = try(each.value.domain_name, var.defaults.domain_name, "") 40 | ebs_options = try(each.value.ebs_options, var.defaults.ebs_options, { 41 | ebs_enabled = true 42 | volume_size = 64 43 | volume_type = "gp3" 44 | }) 45 | enable_access_policy = try(each.value.enable_access_policy, var.defaults.enable_access_policy, true) 46 | encrypt_at_rest = try(each.value.encrypt_at_rest, var.defaults.encrypt_at_rest, { 47 | enabled = true 48 | }) 49 | engine_version = try(each.value.engine_version, var.defaults.engine_version, null) 50 | identity_center_options = try(each.value.identity_center_options, var.defaults.identity_center_options, null) 51 | ip_address_type = try(each.value.ip_address_type, var.defaults.ip_address_type, null) 52 | log_publishing_options = try(each.value.log_publishing_options, var.defaults.log_publishing_options, [ 53 | { log_type = "INDEX_SLOW_LOGS" }, 54 | { log_type = "SEARCH_SLOW_LOGS" }, 55 | ]) 56 | node_to_node_encryption = try(each.value.node_to_node_encryption, var.defaults.node_to_node_encryption, { 57 | enabled = true 58 | }) 59 | off_peak_window_options = try(each.value.off_peak_window_options, var.defaults.off_peak_window_options, { 60 | enabled = true 61 | off_peak_window = { 62 | hours = 7 63 | } 64 | }) 65 | outbound_connections = try(each.value.outbound_connections, var.defaults.outbound_connections, {}) 66 | package_associations = try(each.value.package_associations, var.defaults.package_associations, {}) 67 | region = try(each.value.region, var.defaults.region, null) 68 | saml_options = try(each.value.saml_options, var.defaults.saml_options, {}) 69 | security_group_description = try(each.value.security_group_description, var.defaults.security_group_description, null) 70 | security_group_name = try(each.value.security_group_name, var.defaults.security_group_name, null) 71 | security_group_rules = try(each.value.security_group_rules, var.defaults.security_group_rules, {}) 72 | security_group_tags = try(each.value.security_group_tags, var.defaults.security_group_tags, {}) 73 | security_group_use_name_prefix = try(each.value.security_group_use_name_prefix, var.defaults.security_group_use_name_prefix, true) 74 | software_update_options = try(each.value.software_update_options, var.defaults.software_update_options, { 75 | auto_software_update_enabled = true 76 | }) 77 | tags = try(each.value.tags, var.defaults.tags, {}) 78 | timeouts = try(each.value.timeouts, var.defaults.timeouts, {}) 79 | vpc_endpoints = try(each.value.vpc_endpoints, var.defaults.vpc_endpoints, {}) 80 | vpc_options = try(each.value.vpc_options, var.defaults.vpc_options, {}) 81 | } 82 | -------------------------------------------------------------------------------- /modules/collection/variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Determines whether resources will be created (affects all resources)" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "region" { 8 | description = "Region where this resource will be managed. Defaults to the Region set in the provider configuration" 9 | type = string 10 | default = null 11 | } 12 | 13 | variable "tags" { 14 | description = "A map of tags to add to all resources" 15 | type = map(string) 16 | default = {} 17 | } 18 | 19 | ################################################################################ 20 | # Collection 21 | ################################################################################ 22 | 23 | variable "description" { 24 | description = "Description of the collection" 25 | type = string 26 | default = null 27 | } 28 | 29 | variable "name" { 30 | description = "Name of the collection" 31 | type = string 32 | default = "" 33 | } 34 | 35 | variable "type" { 36 | description = "Type of collection. One of `SEARCH`, `TIMESERIES`, or `VECTORSEARCH`. Defaults to `TIMESERIES`" 37 | type = string 38 | default = null 39 | } 40 | 41 | variable "standby_replicas" { 42 | description = "Indicates whether standby replicas should be used for a collection. One of ENABLED or DISABLED. Defaults to ENABLED." 43 | type = string 44 | default = null 45 | } 46 | 47 | variable "timeouts" { 48 | description = "Create and delete timeout configurations for the collection" 49 | type = map(string) 50 | default = {} 51 | } 52 | 53 | ################################################################################ 54 | # Encryption Policy 55 | ################################################################################ 56 | 57 | variable "create_encryption_policy" { 58 | description = "Determines whether an encryption policy will be created" 59 | type = bool 60 | default = true 61 | } 62 | 63 | variable "encryption_policy_description" { 64 | description = "Description of the encryption policy" 65 | type = string 66 | default = null 67 | } 68 | 69 | variable "encryption_policy_name" { 70 | description = "Name of the encryption policy" 71 | type = string 72 | default = null 73 | } 74 | 75 | variable "encryption_policy" { 76 | description = "Encryption policy to apply to the collection" 77 | type = any 78 | default = {} 79 | } 80 | 81 | ################################################################################ 82 | # Network Policy 83 | ################################################################################ 84 | 85 | variable "create_network_policy" { 86 | description = "Determines whether an network policy will be created" 87 | type = bool 88 | default = false 89 | } 90 | 91 | variable "network_policy_description" { 92 | description = "Description of the network policy" 93 | type = string 94 | default = null 95 | } 96 | 97 | variable "network_policy_name" { 98 | description = "Name of the network policy" 99 | type = string 100 | default = null 101 | } 102 | 103 | variable "network_policy" { 104 | description = "Network policy to apply to the collection" 105 | type = any 106 | default = {} 107 | } 108 | 109 | ################################################################################ 110 | # Access Policy 111 | ################################################################################ 112 | 113 | variable "create_access_policy" { 114 | description = "Determines whether an access policy will be created" 115 | type = bool 116 | default = false 117 | } 118 | 119 | variable "access_policy_description" { 120 | description = "Description of the access policy" 121 | type = string 122 | default = null 123 | } 124 | 125 | variable "access_policy_name" { 126 | description = "Name of the access policy" 127 | type = string 128 | default = null 129 | } 130 | 131 | variable "access_policy_index_permissions" { 132 | description = "Access policy permissions for the collection index" 133 | type = list(string) 134 | default = ["aoss:*"] 135 | } 136 | 137 | variable "access_policy_collection_permissions" { 138 | description = "Access policy permissions for the collection" 139 | type = list(string) 140 | default = ["aoss:*"] 141 | } 142 | 143 | variable "access_policy_principals" { 144 | description = "Access policy principals" 145 | type = list(string) 146 | default = [] 147 | } 148 | 149 | variable "access_policy" { 150 | description = "Access policy to apply to the collection" 151 | type = any 152 | default = {} 153 | } 154 | 155 | ################################################################################ 156 | # Lifecycle Policy 157 | ################################################################################ 158 | 159 | variable "create_lifecycle_policy" { 160 | description = "Determines whether an lifecycle policy will be created" 161 | type = bool 162 | default = false 163 | } 164 | 165 | variable "lifecycle_policy_description" { 166 | description = "Description of the lifecycle policy" 167 | type = string 168 | default = null 169 | } 170 | 171 | variable "lifecycle_policy_name" { 172 | description = "Name of the lifecycle policy" 173 | type = string 174 | default = null 175 | } 176 | 177 | variable "lifecycle_policy_min_index_retention" { 178 | description = "The minimum period, in days (d) or hours (h), to retain the document in the index. The lower bound is `24h` and the upper bound is `3650d`" 179 | type = string 180 | default = null 181 | } 182 | 183 | variable "lifecycle_policy_no_min_index_retention" { 184 | description = "If true, OpenSearch Serverless retains documents indefinitely" 185 | type = bool 186 | default = null 187 | } 188 | -------------------------------------------------------------------------------- /examples/collection/outputs.tf: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # OpenSearch Collection 3 | ################################################################################ 4 | 5 | output "opensearch_collection_public_arn" { 6 | description = "Amazon Resource Name (ARN) of the collection" 7 | value = module.opensearch_collection_public.arn 8 | } 9 | 10 | output "opensearch_collection_public_endpoint" { 11 | description = "Collection-specific endpoint used to submit index, search, and data upload requests to an OpenSearch Serverless collection" 12 | value = module.opensearch_collection_public.endpoint 13 | } 14 | 15 | output "opensearch_collection_public_dashboard_endpoint" { 16 | description = "Collection-specific endpoint used to access OpenSearch Dashboards" 17 | value = module.opensearch_collection_public.dashboard_endpoint 18 | } 19 | 20 | output "opensearch_collection_public_kms_key_arn" { 21 | description = "The ARN of the Amazon Web Services KMS key used to encrypt the collection" 22 | value = module.opensearch_collection_public.kms_key_arn 23 | } 24 | 25 | output "opensearch_collection_public_id" { 26 | description = "Unique identifier for the collection" 27 | value = module.opensearch_collection_public.id 28 | } 29 | 30 | output "opensearch_collection_public_name" { 31 | description = "Name of the collection" 32 | value = module.opensearch_collection_public.name 33 | } 34 | 35 | output "opensearch_collection_public_encryption_policy_version" { 36 | description = "The version of the encryption policy" 37 | value = module.opensearch_collection_public.encryption_policy_version 38 | } 39 | 40 | output "opensearch_collection_public_encryption_policy" { 41 | description = "The JSON policy document of the encryption policy" 42 | value = module.opensearch_collection_public.encryption_policy 43 | } 44 | 45 | output "opensearch_collection_public_network_policy_version" { 46 | description = "The version of the network policy" 47 | value = module.opensearch_collection_public.network_policy_version 48 | } 49 | 50 | output "opensearch_collection_public_network_policy" { 51 | description = "The JSON policy document of the network policy" 52 | value = module.opensearch_collection_public.network_policy 53 | } 54 | 55 | output "opensearch_collection_public_access_policy_version" { 56 | description = "The version of the access policy" 57 | value = module.opensearch_collection_public.access_policy_version 58 | } 59 | 60 | output "opensearch_collection_public_access_policy" { 61 | description = "The JSON policy document of the access policy" 62 | value = module.opensearch_collection_public.access_policy 63 | } 64 | 65 | output "opensearch_collection_public_lifecycle_policy_version" { 66 | description = "The version of the lifecycle policy" 67 | value = module.opensearch_collection_public.lifecycle_policy_version 68 | } 69 | 70 | output "opensearch_collection_public_lifecycle_policy" { 71 | description = "The JSON policy document of the lifecycle policy" 72 | value = module.opensearch_collection_public.lifecycle_policy 73 | } 74 | 75 | ################################################################################ 76 | # OpenSearch Private Collection 77 | ################################################################################ 78 | 79 | output "opensearch_collection_private_arn" { 80 | description = "Amazon Resource Name (ARN) of the collection" 81 | value = module.opensearch_collection_private.arn 82 | } 83 | 84 | output "opensearch_collection_private_endpoint" { 85 | description = "Collection-specific endpoint used to submit index, search, and data upload requests to an OpenSearch Serverless collection" 86 | value = module.opensearch_collection_private.endpoint 87 | } 88 | 89 | output "opensearch_collection_private_dashboard_endpoint" { 90 | description = "Collection-specific endpoint used to access OpenSearch Dashboards" 91 | value = module.opensearch_collection_private.dashboard_endpoint 92 | } 93 | 94 | output "opensearch_collection_private_kms_key_arn" { 95 | description = "The ARN of the Amazon Web Services KMS key used to encrypt the collection" 96 | value = module.opensearch_collection_private.kms_key_arn 97 | } 98 | 99 | output "opensearch_collection_private_id" { 100 | description = "Unique identifier for the collection" 101 | value = module.opensearch_collection_private.id 102 | } 103 | 104 | output "opensearch_collection_private_name" { 105 | description = "Name of the collection" 106 | value = module.opensearch_collection_private.name 107 | } 108 | 109 | output "opensearch_collection_private_encryption_policy_version" { 110 | description = "The version of the encryption policy" 111 | value = module.opensearch_collection_private.encryption_policy_version 112 | } 113 | 114 | output "opensearch_collection_private_encryption_policy" { 115 | description = "The JSON policy document of the encryption policy" 116 | value = module.opensearch_collection_private.encryption_policy 117 | } 118 | 119 | output "opensearch_collection_private_network_policy_version" { 120 | description = "The version of the network policy" 121 | value = module.opensearch_collection_private.network_policy_version 122 | } 123 | 124 | output "opensearch_collection_private_network_policy" { 125 | description = "The JSON policy document of the network policy" 126 | value = module.opensearch_collection_private.network_policy 127 | } 128 | 129 | output "opensearch_collection_private_access_policy_version" { 130 | description = "The version of the access policy" 131 | value = module.opensearch_collection_private.access_policy_version 132 | } 133 | 134 | output "opensearch_collection_private_access_policy" { 135 | description = "The JSON policy document of the access policy" 136 | value = module.opensearch_collection_private.access_policy 137 | } 138 | 139 | output "opensearch_collection_private_lifecycle_policy_version" { 140 | description = "The version of the lifecycle policy" 141 | value = module.opensearch_collection_private.lifecycle_policy_version 142 | } 143 | 144 | output "opensearch_collection_private_lifecycle_policy" { 145 | description = "The JSON policy document of the lifecycle policy" 146 | value = module.opensearch_collection_private.lifecycle_policy 147 | } 148 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Commit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - master 8 | 9 | env: 10 | TERRAFORM_DOCS_VERSION: v0.20.0 11 | TFLINT_VERSION: v0.59.1 12 | 13 | jobs: 14 | collectInputs: 15 | name: Collect workflow inputs 16 | runs-on: ubuntu-latest 17 | outputs: 18 | directories: ${{ steps.dirs.outputs.directories }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v5 22 | 23 | - name: Get root directories 24 | id: dirs 25 | uses: clowdhaus/terraform-composite-actions/directories@v1.14.0 26 | 27 | preCommitMinVersions: 28 | name: Min TF pre-commit 29 | needs: collectInputs 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | directory: ${{ fromJson(needs.collectInputs.outputs.directories) }} 34 | steps: 35 | - name: Install rmz 36 | uses: jaxxstorm/action-install-gh-release@v2.1.0 37 | with: 38 | repo: SUPERCILEX/fuc 39 | asset-name: x86_64-unknown-linux-gnu-rmz 40 | rename-to: rmz 41 | chmod: 0755 42 | extension-matching: disable 43 | 44 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 45 | - name: Delete unnecessary files 46 | run: | 47 | formatByteCount() { echo $(numfmt --to=iec-i --suffix=B --padding=7 $1'000'); } 48 | getAvailableSpace() { echo $(df -a $1 | awk 'NR > 1 {avail+=$4} END {print avail}'); } 49 | 50 | BEFORE=$(getAvailableSpace) 51 | 52 | ln -s /opt/hostedtoolcache/SUPERCILEX/x86_64-unknown-linux-gnu-rmz/latest/linux-x64/rmz /usr/local/bin/rmz 53 | rmz -f /opt/hostedtoolcache/CodeQL & 54 | rmz -f /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk & 55 | rmz -f /opt/hostedtoolcache/PyPy & 56 | rmz -f /opt/hostedtoolcache/Ruby & 57 | rmz -f /opt/hostedtoolcache/go & 58 | 59 | wait 60 | 61 | AFTER=$(getAvailableSpace) 62 | SAVED=$((AFTER-BEFORE)) 63 | echo "=> Saved $(formatByteCount $SAVED)" 64 | 65 | - name: Checkout 66 | uses: actions/checkout@v5 67 | 68 | - name: Terraform min/max versions 69 | id: minMax 70 | uses: clowdhaus/terraform-min-max@v2.1.0 71 | with: 72 | directory: ${{ matrix.directory }} 73 | 74 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 75 | # Run only validate pre-commit check on min version supported 76 | if: ${{ matrix.directory != '.' }} 77 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.14.0 78 | with: 79 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 80 | tflint-version: ${{ env.TFLINT_VERSION }} 81 | args: 'terraform_validate --color=always --show-diff-on-failure --files ${{ matrix.directory }}/*' 82 | 83 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.minVersion }} 84 | # Run only validate pre-commit check on min version supported 85 | if: ${{ matrix.directory == '.' }} 86 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.14.0 87 | with: 88 | terraform-version: ${{ steps.minMax.outputs.minVersion }} 89 | tflint-version: ${{ env.TFLINT_VERSION }} 90 | args: 'terraform_validate --color=always --show-diff-on-failure --files $(ls *.tf)' 91 | 92 | preCommitMaxVersion: 93 | name: Max TF pre-commit 94 | runs-on: ubuntu-latest 95 | needs: collectInputs 96 | steps: 97 | - name: Install rmz 98 | uses: jaxxstorm/action-install-gh-release@v2.1.0 99 | with: 100 | repo: SUPERCILEX/fuc 101 | asset-name: x86_64-unknown-linux-gnu-rmz 102 | rename-to: rmz 103 | chmod: 0755 104 | extension-matching: disable 105 | 106 | # https://github.com/orgs/community/discussions/25678#discussioncomment-5242449 107 | - name: Delete unnecessary files 108 | run: | 109 | formatByteCount() { echo $(numfmt --to=iec-i --suffix=B --padding=7 $1'000'); } 110 | getAvailableSpace() { echo $(df -a $1 | awk 'NR > 1 {avail+=$4} END {print avail}'); } 111 | 112 | BEFORE=$(getAvailableSpace) 113 | 114 | ln -s /opt/hostedtoolcache/SUPERCILEX/x86_64-unknown-linux-gnu-rmz/latest/linux-x64/rmz /usr/local/bin/rmz 115 | rmz -f /opt/hostedtoolcache/CodeQL & 116 | rmz -f /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk & 117 | rmz -f /opt/hostedtoolcache/PyPy & 118 | rmz -f /opt/hostedtoolcache/Ruby & 119 | rmz -f /opt/hostedtoolcache/go & 120 | sudo rmz -f /usr/local/lib/android & 121 | 122 | if [[ ${{ github.repository }} == terraform-aws-modules/terraform-aws-security-group ]]; then 123 | sudo rmz -f /usr/share/dotnet & 124 | sudo rmz -f /usr/local/.ghcup & 125 | sudo apt-get -qq remove -y 'azure-.*' 126 | sudo apt-get -qq remove -y 'cpp-.*' 127 | sudo apt-get -qq remove -y 'dotnet-runtime-.*' 128 | sudo apt-get -qq remove -y 'google-.*' 129 | sudo apt-get -qq remove -y 'libclang-.*' 130 | sudo apt-get -qq remove -y 'libllvm.*' 131 | sudo apt-get -qq remove -y 'llvm-.*' 132 | sudo apt-get -qq remove -y 'mysql-.*' 133 | sudo apt-get -qq remove -y 'postgresql-.*' 134 | sudo apt-get -qq remove -y 'php.*' 135 | sudo apt-get -qq remove -y 'temurin-.*' 136 | sudo apt-get -qq remove -y kubectl firefox mono-devel 137 | sudo apt-get -qq autoremove -y 138 | sudo apt-get -qq clean 139 | fi 140 | 141 | wait 142 | 143 | AFTER=$(getAvailableSpace) 144 | SAVED=$((AFTER-BEFORE)) 145 | echo "=> Saved $(formatByteCount $SAVED)" 146 | 147 | - name: Checkout 148 | uses: actions/checkout@v5 149 | with: 150 | ref: ${{ github.event.pull_request.head.ref }} 151 | repository: ${{github.event.pull_request.head.repo.full_name}} 152 | 153 | - name: Terraform min/max versions 154 | id: minMax 155 | uses: clowdhaus/terraform-min-max@v2.1.0 156 | 157 | - name: Hide template dir 158 | # Special to this repo, we don't want to check this dir 159 | if: ${{ github.repository == 'terraform-aws-modules/terraform-aws-security-group' }} 160 | run: rm -rf modules/_templates 161 | 162 | - name: Pre-commit Terraform ${{ steps.minMax.outputs.maxVersion }} 163 | uses: clowdhaus/terraform-composite-actions/pre-commit@v1.14.0 164 | with: 165 | terraform-version: ${{ steps.minMax.outputs.maxVersion }} 166 | tflint-version: ${{ env.TFLINT_VERSION }} 167 | terraform-docs-version: ${{ env.TERRAFORM_DOCS_VERSION }} 168 | install-hcledit: true 169 | -------------------------------------------------------------------------------- /examples/collection/README.md: -------------------------------------------------------------------------------- 1 | # AWS OpenSearch Serverless Collection Example 2 | 3 | Configuration in this directory creates an AWS OpenSearch serverless collection 4 | 5 | ## Usage 6 | 7 | To run this example you need to execute: 8 | 9 | ```bash 10 | $ terraform init 11 | $ terraform plan 12 | $ terraform apply 13 | ``` 14 | 15 | Note that this example may create resources which will incur monetary charges on your AWS bill. Run `terraform destroy` when you no longer need these resources. 16 | 17 | 18 | ## Requirements 19 | 20 | | Name | Version | 21 | |------|---------| 22 | | [terraform](#requirement\_terraform) | >= 1.5.7 | 23 | | [aws](#requirement\_aws) | >= 6.20 | 24 | 25 | ## Providers 26 | 27 | | Name | Version | 28 | |------|---------| 29 | | [aws](#provider\_aws) | >= 6.20 | 30 | 31 | ## Modules 32 | 33 | | Name | Source | Version | 34 | |------|--------|---------| 35 | | [opensearch\_collection\_disabled](#module\_opensearch\_collection\_disabled) | ../../modules/collection | n/a | 36 | | [opensearch\_collection\_private](#module\_opensearch\_collection\_private) | ../../modules/collection | n/a | 37 | | [opensearch\_collection\_public](#module\_opensearch\_collection\_public) | ../../modules/collection | n/a | 38 | | [vpc](#module\_vpc) | terraform-aws-modules/vpc/aws | ~> 6.0 | 39 | 40 | ## Resources 41 | 42 | | Name | Type | 43 | |------|------| 44 | | [aws_opensearchserverless_vpc_endpoint.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_vpc_endpoint) | resource | 45 | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | 46 | 47 | ## Inputs 48 | 49 | No inputs. 50 | 51 | ## Outputs 52 | 53 | | Name | Description | 54 | |------|-------------| 55 | | [opensearch\_collection\_private\_access\_policy](#output\_opensearch\_collection\_private\_access\_policy) | The JSON policy document of the access policy | 56 | | [opensearch\_collection\_private\_access\_policy\_version](#output\_opensearch\_collection\_private\_access\_policy\_version) | The version of the access policy | 57 | | [opensearch\_collection\_private\_arn](#output\_opensearch\_collection\_private\_arn) | Amazon Resource Name (ARN) of the collection | 58 | | [opensearch\_collection\_private\_dashboard\_endpoint](#output\_opensearch\_collection\_private\_dashboard\_endpoint) | Collection-specific endpoint used to access OpenSearch Dashboards | 59 | | [opensearch\_collection\_private\_encryption\_policy](#output\_opensearch\_collection\_private\_encryption\_policy) | The JSON policy document of the encryption policy | 60 | | [opensearch\_collection\_private\_encryption\_policy\_version](#output\_opensearch\_collection\_private\_encryption\_policy\_version) | The version of the encryption policy | 61 | | [opensearch\_collection\_private\_endpoint](#output\_opensearch\_collection\_private\_endpoint) | Collection-specific endpoint used to submit index, search, and data upload requests to an OpenSearch Serverless collection | 62 | | [opensearch\_collection\_private\_id](#output\_opensearch\_collection\_private\_id) | Unique identifier for the collection | 63 | | [opensearch\_collection\_private\_kms\_key\_arn](#output\_opensearch\_collection\_private\_kms\_key\_arn) | The ARN of the Amazon Web Services KMS key used to encrypt the collection | 64 | | [opensearch\_collection\_private\_lifecycle\_policy](#output\_opensearch\_collection\_private\_lifecycle\_policy) | The JSON policy document of the lifecycle policy | 65 | | [opensearch\_collection\_private\_lifecycle\_policy\_version](#output\_opensearch\_collection\_private\_lifecycle\_policy\_version) | The version of the lifecycle policy | 66 | | [opensearch\_collection\_private\_name](#output\_opensearch\_collection\_private\_name) | Name of the collection | 67 | | [opensearch\_collection\_private\_network\_policy](#output\_opensearch\_collection\_private\_network\_policy) | The JSON policy document of the network policy | 68 | | [opensearch\_collection\_private\_network\_policy\_version](#output\_opensearch\_collection\_private\_network\_policy\_version) | The version of the network policy | 69 | | [opensearch\_collection\_public\_access\_policy](#output\_opensearch\_collection\_public\_access\_policy) | The JSON policy document of the access policy | 70 | | [opensearch\_collection\_public\_access\_policy\_version](#output\_opensearch\_collection\_public\_access\_policy\_version) | The version of the access policy | 71 | | [opensearch\_collection\_public\_arn](#output\_opensearch\_collection\_public\_arn) | Amazon Resource Name (ARN) of the collection | 72 | | [opensearch\_collection\_public\_dashboard\_endpoint](#output\_opensearch\_collection\_public\_dashboard\_endpoint) | Collection-specific endpoint used to access OpenSearch Dashboards | 73 | | [opensearch\_collection\_public\_encryption\_policy](#output\_opensearch\_collection\_public\_encryption\_policy) | The JSON policy document of the encryption policy | 74 | | [opensearch\_collection\_public\_encryption\_policy\_version](#output\_opensearch\_collection\_public\_encryption\_policy\_version) | The version of the encryption policy | 75 | | [opensearch\_collection\_public\_endpoint](#output\_opensearch\_collection\_public\_endpoint) | Collection-specific endpoint used to submit index, search, and data upload requests to an OpenSearch Serverless collection | 76 | | [opensearch\_collection\_public\_id](#output\_opensearch\_collection\_public\_id) | Unique identifier for the collection | 77 | | [opensearch\_collection\_public\_kms\_key\_arn](#output\_opensearch\_collection\_public\_kms\_key\_arn) | The ARN of the Amazon Web Services KMS key used to encrypt the collection | 78 | | [opensearch\_collection\_public\_lifecycle\_policy](#output\_opensearch\_collection\_public\_lifecycle\_policy) | The JSON policy document of the lifecycle policy | 79 | | [opensearch\_collection\_public\_lifecycle\_policy\_version](#output\_opensearch\_collection\_public\_lifecycle\_policy\_version) | The version of the lifecycle policy | 80 | | [opensearch\_collection\_public\_name](#output\_opensearch\_collection\_public\_name) | Name of the collection | 81 | | [opensearch\_collection\_public\_network\_policy](#output\_opensearch\_collection\_public\_network\_policy) | The JSON policy document of the network policy | 82 | | [opensearch\_collection\_public\_network\_policy\_version](#output\_opensearch\_collection\_public\_network\_policy\_version) | The version of the network policy | 83 | 84 | 85 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-opensearch/blob/master/LICENSE). 86 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [2.4.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v2.3.0...v2.4.0) (2025-11-16) 6 | 7 | ### Features 8 | 9 | * Support `aws_opensearch_domain.identity_center_options` ([#50](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/50)) ([1b5f215](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/1b5f21539d0e05e2e101a108778fc416eb331e08)) 10 | 11 | ## [2.3.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v2.2.1...v2.3.0) (2025-10-23) 12 | 13 | ### Features 14 | 15 | * Add `domain_name` output ([#48](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/48)) ([c102fd0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/c102fd07f604768eb85140db7cf2d921291dea3b)) 16 | 17 | ## [2.2.1](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v2.2.0...v2.2.1) (2025-10-21) 18 | 19 | ### Bug Fixes 20 | 21 | * Update CI workflow versions to latest ([#47](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/47)) ([a0cb6b2](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/a0cb6b2968146b04e9af82c4b86a35b4fa3a26c2)) 22 | 23 | ## [2.2.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v2.1.1...v2.2.0) (2025-10-04) 24 | 25 | 26 | ### Features 27 | 28 | * Support AIML Options ([#45](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/45)) ([845c1a3](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/845c1a3060e7da2d4af16fde64d15598d3137cda)) 29 | 30 | ## [2.1.1](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v2.1.0...v2.1.1) (2025-09-01) 31 | 32 | 33 | ### Bug Fixes 34 | 35 | * Add region for subnets recovery ([#44](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/44)) ([e019891](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/e01989196f320ab8964f21300f53675faf27a297)) 36 | 37 | ## [2.1.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v2.0.0...v2.1.0) (2025-08-27) 38 | 39 | 40 | ### Features 41 | 42 | * Add variable validation rules to `engine_version` to prevent invalid formats ([#42](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/42)) ([8fd929b](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/8fd929ba7c7dba3b5e90a430302b09cb5d398d7d)) 43 | 44 | ## [2.0.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.7.0...v2.0.0) (2025-06-27) 45 | 46 | 47 | ### ⚠ BREAKING CHANGES 48 | 49 | * MSV AWS Provider v6 (#39) 50 | 51 | ### Features 52 | 53 | * MSV AWS Provider v6 ([#39](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/39)) ([f99fa7e](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/f99fa7eb38dc73229212d86f6712755b23d1bef0)) 54 | 55 | ## [1.7.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.6.0...v1.7.0) (2025-03-30) 56 | 57 | 58 | ### Features 59 | 60 | * Support `aws_opensearch_domain.node_options` ([#37](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/37)) ([2a14c3f](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/2a14c3fe32471c301b0892255646bf2f1ea8476b)) 61 | 62 | ## [1.6.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.5.0...v1.6.0) (2025-01-26) 63 | 64 | 65 | ### Features 66 | 67 | * Add `collection_name` to outputs ([#35](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/35)) ([111032e](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/111032e0f468b0201776a72dbe32e39c35e5ab1b)) 68 | 69 | ## [1.5.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.4.1...v1.5.0) (2024-12-01) 70 | 71 | 72 | ### Features 73 | 74 | * Add `domain_endpoint_v2_hosted_zone_id` to outputs ([#33](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/33)) ([7440825](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/744082557409584374db15bc1d71f412dbcd21e4)) 75 | 76 | ## [1.4.1](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.4.0...v1.4.1) (2024-11-29) 77 | 78 | 79 | ### Bug Fixes 80 | 81 | * Skip `zone_awareness_config` if `zone_awareness` is not enabled ([#32](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/32)) ([1ff02d2](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/1ff02d2ffd1feae6afdf85a4c493e5cd2acff014)) 82 | * Update CI workflow versions to latest ([#29](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/29)) ([dde73c8](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/dde73c841f48467127ba2beef49bd2c6d9728853)) 83 | 84 | ## [1.4.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.3.1...v1.4.0) (2024-08-28) 85 | 86 | 87 | ### Features 88 | 89 | * Added dual-stack domain endpoints to outputs ([#23](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/23)) ([4e09835](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/4e098353df20c1d1768e4126c832a7d7fb8b7adb)) 90 | 91 | ## [1.3.1](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.3.0...v1.3.1) (2024-07-05) 92 | 93 | 94 | ### Bug Fixes 95 | 96 | * Correct evaluation logic when determining `master_user_options` arguments ([#20](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/20)) ([7d00ee2](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/7d00ee201b66be008bd64a7ca520055e573a7194)) 97 | 98 | ## [1.3.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.2.2...v1.3.0) (2024-07-05) 99 | 100 | 101 | ### Features 102 | 103 | * Add timeout for opensearch domain ([#21](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/21)) ([e8ff403](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/e8ff403c1d05416f5f93156c89cd2026767ee5da)) 104 | 105 | ## [1.2.2](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.2.1...v1.2.2) (2024-06-28) 106 | 107 | 108 | ### Bug Fixes 109 | 110 | * Correct mis-spelling in serverless security policy default description ([#16](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/16)) ([844a435](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/844a4354734c7f3f74e412aa001844955d1031e6)) 111 | 112 | ## [1.2.1](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.2.0...v1.2.1) (2024-06-28) 113 | 114 | 115 | ### Bug Fixes 116 | 117 | * Create a domain policy that implicitly depends_on domain ([#15](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/15)) ([473ba5d](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/473ba5df25e8b1b480c7dc661361b39c2ac8ce6d)) 118 | 119 | ## [1.2.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.1.2...v1.2.0) (2024-06-19) 120 | 121 | 122 | ### Features 123 | 124 | * Opensearch Domain `ip_address_type`, Cloudwatch `log_group_class`, and `skip_destroy` ([#13](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/13)) ([b7ab787](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/b7ab7872ba8e4508b7e9879fe0608745ce369af0)) 125 | 126 | ## [1.1.2](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.1.1...v1.1.2) (2024-04-09) 127 | 128 | 129 | ### Bug Fixes 130 | 131 | * Do not create default zone_awareness_config if it not set ([#3](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/3)) ([3a1f79f](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/3a1f79f1fa06a660091af7fa1843303f47f54c0f)) 132 | 133 | ## [1.1.1](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.1.0...v1.1.1) (2024-04-09) 134 | 135 | 136 | ### Bug Fixes 137 | 138 | * Fix the condition of the logical operators of `master_user_name` and `master_user_password` ([#4](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/4)) ([fa811d2](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/fa811d29e80f1658b8af148c8d0697b29fbc6593)) 139 | 140 | ## [1.1.0](https://github.com/terraform-aws-modules/terraform-aws-opensearch/compare/v1.0.0...v1.1.0) (2024-03-22) 141 | 142 | 143 | ### Features 144 | 145 | * Add `standby_replicas` to serverless collection and `auto_tune_options.use_off_peak_window` to domain ([#2](https://github.com/terraform-aws-modules/terraform-aws-opensearch/issues/2)) ([dff3f63](https://github.com/terraform-aws-modules/terraform-aws-opensearch/commit/dff3f6357cbe92f582527267499f82cf90d6027e)) 146 | -------------------------------------------------------------------------------- /modules/collection/README.md: -------------------------------------------------------------------------------- 1 | # AWS OpenSearch Collection Terraform module 2 | 3 | Terraform module which creates AWS OpenSearch collection resources. 4 | 5 | ## Usage 6 | 7 | See [`examples`](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples) directory for working examples to reference: 8 | 9 | ```hcl 10 | module "opensearch_serverless" { 11 | source = "terraform-aws-modules/opensearch/aws//modules/collection" 12 | 13 | name = "example" 14 | description = "Example OpenSearch Serverless collection" 15 | type = "SEARCH" 16 | 17 | create_access_policy = true 18 | create_network_policy = true 19 | network_policy = { 20 | AllowFromPublic = false 21 | SourceVPCEs = [ 22 | "vpce-050f79086ee71ac05" 23 | ] 24 | } 25 | 26 | tags = { 27 | Terraform = "true" 28 | Environment = "dev" 29 | } 30 | } 31 | ``` 32 | ## Examples 33 | 34 | Examples codified under the [`examples`](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples) are intended to give users references for how to use the module(s) as well as testing/validating changes to the source code of the module. If contributing to the project, please be sure to make any appropriate updates to the relevant examples to allow maintainers to test your changes and to keep the examples up to date for users. Thank you! 35 | 36 | - [Complete](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples/complete) 37 | - [Serverless](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples/collection) 38 | 39 | 40 | ## Requirements 41 | 42 | | Name | Version | 43 | |------|---------| 44 | | [terraform](#requirement\_terraform) | >= 1.5.7 | 45 | | [aws](#requirement\_aws) | >= 6.20 | 46 | 47 | ## Providers 48 | 49 | | Name | Version | 50 | |------|---------| 51 | | [aws](#provider\_aws) | >= 6.20 | 52 | 53 | ## Modules 54 | 55 | No modules. 56 | 57 | ## Resources 58 | 59 | | Name | Type | 60 | |------|------| 61 | | [aws_opensearchserverless_access_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_access_policy) | resource | 62 | | [aws_opensearchserverless_collection.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_collection) | resource | 63 | | [aws_opensearchserverless_lifecycle_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_lifecycle_policy) | resource | 64 | | [aws_opensearchserverless_security_policy.encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_security_policy) | resource | 65 | | [aws_opensearchserverless_security_policy.network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_security_policy) | resource | 66 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 67 | 68 | ## Inputs 69 | 70 | | Name | Description | Type | Default | Required | 71 | |------|-------------|------|---------|:--------:| 72 | | [access\_policy](#input\_access\_policy) | Access policy to apply to the collection | `any` | `{}` | no | 73 | | [access\_policy\_collection\_permissions](#input\_access\_policy\_collection\_permissions) | Access policy permissions for the collection | `list(string)` |
[
"aoss:*"
]
| no | 74 | | [access\_policy\_description](#input\_access\_policy\_description) | Description of the access policy | `string` | `null` | no | 75 | | [access\_policy\_index\_permissions](#input\_access\_policy\_index\_permissions) | Access policy permissions for the collection index | `list(string)` |
[
"aoss:*"
]
| no | 76 | | [access\_policy\_name](#input\_access\_policy\_name) | Name of the access policy | `string` | `null` | no | 77 | | [access\_policy\_principals](#input\_access\_policy\_principals) | Access policy principals | `list(string)` | `[]` | no | 78 | | [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | 79 | | [create\_access\_policy](#input\_create\_access\_policy) | Determines whether an access policy will be created | `bool` | `false` | no | 80 | | [create\_encryption\_policy](#input\_create\_encryption\_policy) | Determines whether an encryption policy will be created | `bool` | `true` | no | 81 | | [create\_lifecycle\_policy](#input\_create\_lifecycle\_policy) | Determines whether an lifecycle policy will be created | `bool` | `false` | no | 82 | | [create\_network\_policy](#input\_create\_network\_policy) | Determines whether an network policy will be created | `bool` | `false` | no | 83 | | [description](#input\_description) | Description of the collection | `string` | `null` | no | 84 | | [encryption\_policy](#input\_encryption\_policy) | Encryption policy to apply to the collection | `any` | `{}` | no | 85 | | [encryption\_policy\_description](#input\_encryption\_policy\_description) | Description of the encryption policy | `string` | `null` | no | 86 | | [encryption\_policy\_name](#input\_encryption\_policy\_name) | Name of the encryption policy | `string` | `null` | no | 87 | | [lifecycle\_policy\_description](#input\_lifecycle\_policy\_description) | Description of the lifecycle policy | `string` | `null` | no | 88 | | [lifecycle\_policy\_min\_index\_retention](#input\_lifecycle\_policy\_min\_index\_retention) | The minimum period, in days (d) or hours (h), to retain the document in the index. The lower bound is `24h` and the upper bound is `3650d` | `string` | `null` | no | 89 | | [lifecycle\_policy\_name](#input\_lifecycle\_policy\_name) | Name of the lifecycle policy | `string` | `null` | no | 90 | | [lifecycle\_policy\_no\_min\_index\_retention](#input\_lifecycle\_policy\_no\_min\_index\_retention) | If true, OpenSearch Serverless retains documents indefinitely | `bool` | `null` | no | 91 | | [name](#input\_name) | Name of the collection | `string` | `""` | no | 92 | | [network\_policy](#input\_network\_policy) | Network policy to apply to the collection | `any` | `{}` | no | 93 | | [network\_policy\_description](#input\_network\_policy\_description) | Description of the network policy | `string` | `null` | no | 94 | | [network\_policy\_name](#input\_network\_policy\_name) | Name of the network policy | `string` | `null` | no | 95 | | [region](#input\_region) | Region where this resource will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no | 96 | | [standby\_replicas](#input\_standby\_replicas) | Indicates whether standby replicas should be used for a collection. One of ENABLED or DISABLED. Defaults to ENABLED. | `string` | `null` | no | 97 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | 98 | | [timeouts](#input\_timeouts) | Create and delete timeout configurations for the collection | `map(string)` | `{}` | no | 99 | | [type](#input\_type) | Type of collection. One of `SEARCH`, `TIMESERIES`, or `VECTORSEARCH`. Defaults to `TIMESERIES` | `string` | `null` | no | 100 | 101 | ## Outputs 102 | 103 | | Name | Description | 104 | |------|-------------| 105 | | [access\_policy](#output\_access\_policy) | The JSON policy document of the access policy | 106 | | [access\_policy\_version](#output\_access\_policy\_version) | The version of the access policy | 107 | | [arn](#output\_arn) | Amazon Resource Name (ARN) of the collection | 108 | | [dashboard\_endpoint](#output\_dashboard\_endpoint) | Collection-specific endpoint used to access OpenSearch Dashboards | 109 | | [encryption\_policy](#output\_encryption\_policy) | The JSON policy document of the encryption policy | 110 | | [encryption\_policy\_version](#output\_encryption\_policy\_version) | The version of the encryption policy | 111 | | [endpoint](#output\_endpoint) | Collection-specific endpoint used to submit index, search, and data upload requests to an OpenSearch Serverless collection | 112 | | [id](#output\_id) | Unique identifier for the collection | 113 | | [kms\_key\_arn](#output\_kms\_key\_arn) | The ARN of the Amazon Web Services KMS key used to encrypt the collection | 114 | | [lifecycle\_policy](#output\_lifecycle\_policy) | The JSON policy document of the lifecycle policy | 115 | | [lifecycle\_policy\_version](#output\_lifecycle\_policy\_version) | The version of the lifecycle policy | 116 | | [name](#output\_name) | Name of the collection | 117 | | [network\_policy](#output\_network\_policy) | The JSON policy document of the network policy | 118 | | [network\_policy\_version](#output\_network\_policy\_version) | The version of the network policy | 119 | 120 | 121 | ## License 122 | 123 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-opensearch/blob/master/LICENSE). 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "create" { 2 | description = "Determines whether resources will be created (affects all resources)" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "tags" { 8 | description = "A map of tags to add to all resources" 9 | type = map(string) 10 | default = {} 11 | } 12 | 13 | ################################################################################ 14 | # Domain 15 | ################################################################################ 16 | 17 | variable "advanced_options" { 18 | description = "Key-value string pairs to specify advanced configuration options. Note that the values for these configuration options must be strings (wrapped in quotes) or they may be wrong and cause a perpetual diff, causing Terraform to want to recreate your Elasticsearch domain on every apply" 19 | type = map(string) 20 | default = {} 21 | } 22 | 23 | variable "advanced_security_options" { 24 | description = "Configuration block for [fine-grained access control](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/fgac.html)" 25 | type = any 26 | default = { 27 | enabled = true 28 | anonymous_auth_enabled = false 29 | } 30 | } 31 | 32 | variable "aiml_options" { 33 | description = "Configuration block for Natural Language Query Generation and s3 Vectors" 34 | type = object({ 35 | natural_language_query_generation_options = optional(object({ 36 | desired_state = optional(string) 37 | })) 38 | s3_vectors_engine = optional(object({ 39 | enabled = optional(bool) 40 | })) 41 | }) 42 | default = null 43 | } 44 | 45 | variable "auto_tune_options" { 46 | description = "Configuration block for the Auto-Tune options of the domain" 47 | type = any 48 | default = { 49 | desired_state = "ENABLED" 50 | rollback_on_disable = "NO_ROLLBACK" 51 | } 52 | } 53 | 54 | variable "cluster_config" { 55 | description = "Configuration block for the cluster of the domain" 56 | type = any 57 | default = { 58 | dedicated_master_enabled = true 59 | } 60 | } 61 | 62 | variable "cognito_options" { 63 | description = "Configuration block for authenticating Kibana with Cognito" 64 | type = any 65 | default = {} 66 | } 67 | 68 | variable "domain_endpoint_options" { 69 | description = "Configuration block for domain endpoint HTTP(S) related options" 70 | type = any 71 | default = { 72 | enforce_https = true 73 | tls_security_policy = "Policy-Min-TLS-1-2-2019-07" 74 | } 75 | } 76 | 77 | variable "domain_name" { 78 | description = "Name of the domain" 79 | type = string 80 | default = "" 81 | } 82 | 83 | variable "ebs_options" { 84 | description = "Configuration block for EBS related options, may be required based on chosen [instance size](https://aws.amazon.com/elasticsearch-service/pricing/)" 85 | type = any 86 | default = { 87 | ebs_enabled = true 88 | volume_size = 64 89 | volume_type = "gp3" 90 | } 91 | } 92 | 93 | variable "encrypt_at_rest" { 94 | description = "Configuration block for encrypting at rest" 95 | type = any 96 | default = { 97 | enabled = true 98 | } 99 | } 100 | 101 | variable "engine_version" { 102 | description = "Version of the OpenSearch engine to use. Must follow format 'OpenSearch_X.Y' (e.g., 'OpenSearch_2.11')" 103 | type = string 104 | default = null 105 | 106 | validation { 107 | condition = var.engine_version == null || can(regex("^(Elasticsearch_[0-9]{1}\\.[0-9]{1,2}|OpenSearch_[0-9]{1,2}\\.[0-9]{1,2})$", var.engine_version)) 108 | error_message = <<-EOT 109 | The engine_version must be in the format: 110 | - 'OpenSearch_X.Y' where X is 1-2 digits and Y is 1-2 digits (e.g., 'OpenSearch_2.11', 'OpenSearch_1.3') 111 | - 'Elasticsearch_X.Y' where X is 1 digit and Y is 1-2 digits (e.g., 'Elasticsearch_7.10') 112 | 113 | Your provided value appears to be a software service version (with release dates/patches). 114 | Please use only the engine version format. Common valid versions include: 115 | - OpenSearch_1.0, OpenSearch_1.1, OpenSearch_1.2, OpenSearch_1.3 116 | - OpenSearch_2.0, OpenSearch_2.3, OpenSearch_2.5, OpenSearch_2.7, OpenSearch_2.9, OpenSearch_2.11 117 | EOT 118 | } 119 | } 120 | 121 | variable "ip_address_type" { 122 | description = "The IP address type for the endpoint. Valid values are ipv4 and dualstack" 123 | type = string 124 | default = null 125 | } 126 | 127 | variable "identity_center_options" { 128 | description = "Configuration block for enabling and managing IAM Identity Center integration within a domain" 129 | type = object({ 130 | enabled_api_access = optional(bool) 131 | identity_center_instance_arn = optional(string) 132 | roles_key = optional(string) 133 | subject_key = optional(string) 134 | }) 135 | default = null 136 | } 137 | 138 | variable "log_publishing_options" { 139 | description = "Configuration block for publishing slow and application logs to CloudWatch Logs. This block can be declared multiple times, for each log_type, within the same resource" 140 | type = any 141 | default = [ 142 | { log_type = "INDEX_SLOW_LOGS" }, 143 | { log_type = "SEARCH_SLOW_LOGS" }, 144 | ] 145 | } 146 | 147 | variable "node_to_node_encryption" { 148 | description = "Configuration block for node-to-node encryption options" 149 | type = any 150 | default = { 151 | enabled = true 152 | } 153 | } 154 | 155 | variable "off_peak_window_options" { 156 | description = "Configuration to add Off Peak update options" 157 | type = any 158 | default = { 159 | enabled = true 160 | off_peak_window = { 161 | hours = 7 162 | } 163 | } 164 | } 165 | 166 | variable "region" { 167 | description = "Region where this resource will be managed. Defaults to the Region set in the provider configuration" 168 | type = string 169 | default = null 170 | } 171 | 172 | variable "software_update_options" { 173 | description = "Software update options for the domain" 174 | type = any 175 | default = { 176 | auto_software_update_enabled = true 177 | } 178 | } 179 | 180 | variable "vpc_options" { 181 | description = "Configuration block for VPC related options. Adding or removing this configuration forces a new resource ([documentation](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-vpc.html#es-vpc-limitations))" 182 | type = any 183 | default = {} 184 | } 185 | 186 | variable "timeouts" { 187 | description = "Create and delete timeout configurations for the domain" 188 | type = map(string) 189 | default = {} 190 | } 191 | 192 | ################################################################################ 193 | # Package Association(s) 194 | ################################################################################ 195 | 196 | variable "package_associations" { 197 | description = "Map of package association IDs to associate with the domain" 198 | type = map(string) 199 | default = {} 200 | } 201 | 202 | ################################################################################ 203 | # VPC Endpoint(s) 204 | ################################################################################ 205 | 206 | variable "vpc_endpoints" { 207 | description = "Map of VPC endpoints to create for the domain" 208 | type = any 209 | default = {} 210 | } 211 | 212 | ################################################################################ 213 | # Access Policy 214 | ################################################################################ 215 | 216 | variable "enable_access_policy" { 217 | description = "Determines whether an access policy will be applied to the domain" 218 | type = bool 219 | default = true 220 | } 221 | 222 | variable "create_access_policy" { 223 | description = "Determines whether an access policy will be created" 224 | type = bool 225 | default = true 226 | } 227 | 228 | variable "access_policies" { 229 | description = "IAM policy document specifying the access policies for the domain. Required if `create_access_policy` is `false`" 230 | type = string 231 | default = null 232 | } 233 | 234 | variable "access_policy_statements" { 235 | description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage" 236 | type = any 237 | default = {} 238 | } 239 | 240 | variable "access_policy_source_policy_documents" { 241 | description = "List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s" 242 | type = list(string) 243 | default = [] 244 | } 245 | 246 | variable "access_policy_override_policy_documents" { 247 | description = "List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid`" 248 | type = list(string) 249 | default = [] 250 | } 251 | 252 | ################################################################################ 253 | # SAML Options 254 | ################################################################################ 255 | 256 | variable "create_saml_options" { 257 | description = "Determines whether SAML options will be created" 258 | type = bool 259 | default = false 260 | } 261 | 262 | variable "saml_options" { 263 | description = "SAML authentication options for an AWS OpenSearch Domain" 264 | type = any 265 | default = {} 266 | } 267 | 268 | ################################################################################ 269 | # Outbound Connections 270 | ################################################################################ 271 | 272 | variable "outbound_connections" { 273 | description = "Map of AWS OpenSearch outbound connections to create" 274 | type = any 275 | default = {} 276 | } 277 | 278 | ################################################################################ 279 | # CloudWatch Log Group 280 | ################################################################################ 281 | 282 | variable "create_cloudwatch_log_groups" { 283 | description = "Determines whether log groups are created" 284 | type = bool 285 | default = true 286 | } 287 | 288 | variable "cloudwatch_log_group_retention_in_days" { 289 | description = "Number of days to retain log events" 290 | type = number 291 | default = 60 292 | } 293 | 294 | variable "cloudwatch_log_group_kms_key_id" { 295 | description = "If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html)" 296 | type = string 297 | default = null 298 | } 299 | 300 | variable "cloudwatch_log_group_skip_destroy" { 301 | description = "Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state" 302 | type = bool 303 | default = null 304 | } 305 | 306 | variable "cloudwatch_log_group_class" { 307 | description = "Specified the log class of the log group. Possible values are: STANDARD or INFREQUENT_ACCESS" 308 | type = string 309 | default = null 310 | } 311 | 312 | variable "create_cloudwatch_log_resource_policy" { 313 | description = "Determines whether a resource policy will be created for OpenSearch to log to CloudWatch" 314 | type = bool 315 | default = true 316 | } 317 | 318 | variable "cloudwatch_log_resource_policy_name" { 319 | description = "Name of the resource policy for OpenSearch to log to CloudWatch" 320 | type = string 321 | default = null 322 | } 323 | 324 | ################################################################################ 325 | # Security Group 326 | ################################################################################ 327 | 328 | variable "create_security_group" { 329 | description = "Determines if a security group is created" 330 | type = bool 331 | default = true 332 | } 333 | 334 | variable "security_group_name" { 335 | description = "Name to use on security group created" 336 | type = string 337 | default = null 338 | } 339 | 340 | variable "security_group_use_name_prefix" { 341 | description = "Determines whether the security group name (`security_group_name`) is used as a prefix" 342 | type = bool 343 | default = true 344 | } 345 | 346 | variable "security_group_description" { 347 | description = "Description of the security group created" 348 | type = string 349 | default = null 350 | } 351 | 352 | variable "security_group_rules" { 353 | description = "Security group ingress and egress rules to add to the security group created" 354 | type = any 355 | default = {} 356 | } 357 | 358 | variable "security_group_tags" { 359 | description = "A map of additional tags to add to the security group created" 360 | type = map(string) 361 | default = {} 362 | } 363 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS OpenSearch Terraform module 2 | 3 | Terraform module which creates AWS OpenSearch resources. 4 | 5 | [![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) 6 | 7 | ## Usage 8 | 9 | See [`examples`](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples) directory for working examples to reference: 10 | 11 | ```hcl 12 | module "opensearch" { 13 | source = "terraform-aws-modules/opensearch/aws" 14 | 15 | 16 | # Domain 17 | advanced_options = { 18 | "rest.action.multi.allow_explicit_index" = "true" 19 | } 20 | 21 | advanced_security_options = { 22 | enabled = false 23 | anonymous_auth_enabled = true 24 | internal_user_database_enabled = true 25 | 26 | master_user_options = { 27 | master_user_name = "example" 28 | master_user_password = "Barbarbarbar1!" 29 | } 30 | } 31 | 32 | auto_tune_options = { 33 | desired_state = "ENABLED" 34 | 35 | maintenance_schedule = [ 36 | { 37 | start_at = "2028-05-13T07:44:12Z" 38 | cron_expression_for_recurrence = "cron(0 0 ? * 1 *)" 39 | duration = { 40 | value = "2" 41 | unit = "HOURS" 42 | } 43 | } 44 | ] 45 | 46 | rollback_on_disable = "NO_ROLLBACK" 47 | } 48 | 49 | cluster_config = { 50 | instance_count = 3 51 | dedicated_master_enabled = true 52 | dedicated_master_type = "c6g.large.search" 53 | instance_type = "r6g.large.search" 54 | 55 | zone_awareness_config = { 56 | availability_zone_count = 3 57 | } 58 | 59 | zone_awareness_enabled = true 60 | } 61 | 62 | domain_endpoint_options = { 63 | enforce_https = true 64 | tls_security_policy = "Policy-Min-TLS-1-2-2019-07" 65 | } 66 | 67 | domain_name = local.name 68 | 69 | ebs_options = { 70 | ebs_enabled = true 71 | iops = 3000 72 | throughput = 125 73 | volume_type = "gp3" 74 | volume_size = 20 75 | } 76 | 77 | encrypt_at_rest = { 78 | enabled = true 79 | } 80 | 81 | engine_version = "OpenSearch_2.11" 82 | 83 | log_publishing_options = [ 84 | { log_type = "INDEX_SLOW_LOGS" }, 85 | { log_type = "SEARCH_SLOW_LOGS" }, 86 | ] 87 | 88 | node_to_node_encryption = { 89 | enabled = true 90 | } 91 | 92 | software_update_options = { 93 | auto_software_update_enabled = true 94 | } 95 | 96 | vpc_options = { 97 | subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] 98 | } 99 | 100 | # VPC endpoint 101 | vpc_endpoints = { 102 | one = { 103 | subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"] 104 | } 105 | } 106 | 107 | # Access policy 108 | access_policy_statements = [ 109 | { 110 | effect = "Allow" 111 | 112 | principals = [{ 113 | type = "*" 114 | identifiers = ["*"] 115 | }] 116 | 117 | actions = ["es:*"] 118 | 119 | conditions = [{ 120 | test = "IpAddress" 121 | variable = "aws:SourceIp" 122 | values = ["127.0.0.1/32"] 123 | }] 124 | } 125 | ] 126 | 127 | tags = { 128 | Terraform = "true" 129 | Environment = "dev" 130 | } 131 | } 132 | ``` 133 | 134 | ## Examples 135 | 136 | Examples codified under the [`examples`](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples) are intended to give users references for how to use the module(s) as well as testing/validating changes to the source code of the module. If contributing to the project, please be sure to make any appropriate updates to the relevant examples to allow maintainers to test your changes and to keep the examples up to date for users. Thank you! 137 | 138 | - [Complete](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples/complete) 139 | - [Serverless](https://github.com/terraform-aws-modules/terraform-aws-opensearch/tree/master/examples/collection) 140 | 141 | 142 | ## Requirements 143 | 144 | | Name | Version | 145 | |------|---------| 146 | | [terraform](#requirement\_terraform) | >= 1.5.7 | 147 | | [aws](#requirement\_aws) | >= 6.20 | 148 | 149 | ## Providers 150 | 151 | | Name | Version | 152 | |------|---------| 153 | | [aws](#provider\_aws) | >= 6.20 | 154 | 155 | ## Modules 156 | 157 | No modules. 158 | 159 | ## Resources 160 | 161 | | Name | Type | 162 | |------|------| 163 | | [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | 164 | | [aws_cloudwatch_log_resource_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_resource_policy) | resource | 165 | | [aws_opensearch_domain.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain) | resource | 166 | | [aws_opensearch_domain_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain_policy) | resource | 167 | | [aws_opensearch_domain_saml_options.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain_saml_options) | resource | 168 | | [aws_opensearch_outbound_connection.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_outbound_connection) | resource | 169 | | [aws_opensearch_package_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_package_association) | resource | 170 | | [aws_opensearch_vpc_endpoint.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_vpc_endpoint) | resource | 171 | | [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | 172 | | [aws_vpc_security_group_egress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource | 173 | | [aws_vpc_security_group_ingress_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource | 174 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 175 | | [aws_iam_policy_document.cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 176 | | [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 177 | | [aws_iam_session_context.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_session_context) | data source | 178 | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 179 | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | 180 | | [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | 181 | 182 | ## Inputs 183 | 184 | | Name | Description | Type | Default | Required | 185 | |------|-------------|------|---------|:--------:| 186 | | [access\_policies](#input\_access\_policies) | IAM policy document specifying the access policies for the domain. Required if `create_access_policy` is `false` | `string` | `null` | no | 187 | | [access\_policy\_override\_policy\_documents](#input\_access\_policy\_override\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. In merging, statements with non-blank `sid`s will override statements with the same `sid` | `list(string)` | `[]` | no | 188 | | [access\_policy\_source\_policy\_documents](#input\_access\_policy\_source\_policy\_documents) | List of IAM policy documents that are merged together into the exported document. Statements must have unique `sid`s | `list(string)` | `[]` | no | 189 | | [access\_policy\_statements](#input\_access\_policy\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no | 190 | | [advanced\_options](#input\_advanced\_options) | Key-value string pairs to specify advanced configuration options. Note that the values for these configuration options must be strings (wrapped in quotes) or they may be wrong and cause a perpetual diff, causing Terraform to want to recreate your Elasticsearch domain on every apply | `map(string)` | `{}` | no | 191 | | [advanced\_security\_options](#input\_advanced\_security\_options) | Configuration block for [fine-grained access control](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/fgac.html) | `any` |
{
"anonymous_auth_enabled": false,
"enabled": true
}
| no | 192 | | [aiml\_options](#input\_aiml\_options) | Configuration block for Natural Language Query Generation and s3 Vectors |
object({
natural_language_query_generation_options = optional(object({
desired_state = optional(string)
}))
s3_vectors_engine = optional(object({
enabled = optional(bool)
}))
})
| `null` | no | 193 | | [auto\_tune\_options](#input\_auto\_tune\_options) | Configuration block for the Auto-Tune options of the domain | `any` |
{
"desired_state": "ENABLED",
"rollback_on_disable": "NO_ROLLBACK"
}
| no | 194 | | [cloudwatch\_log\_group\_class](#input\_cloudwatch\_log\_group\_class) | Specified the log class of the log group. Possible values are: STANDARD or INFREQUENT\_ACCESS | `string` | `null` | no | 195 | | [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) | `string` | `null` | no | 196 | | [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events | `number` | `60` | no | 197 | | [cloudwatch\_log\_group\_skip\_destroy](#input\_cloudwatch\_log\_group\_skip\_destroy) | Set to true if you do not wish the log group (and any logs it may contain) to be deleted at destroy time, and instead just remove the log group from the Terraform state | `bool` | `null` | no | 198 | | [cloudwatch\_log\_resource\_policy\_name](#input\_cloudwatch\_log\_resource\_policy\_name) | Name of the resource policy for OpenSearch to log to CloudWatch | `string` | `null` | no | 199 | | [cluster\_config](#input\_cluster\_config) | Configuration block for the cluster of the domain | `any` |
{
"dedicated_master_enabled": true
}
| no | 200 | | [cognito\_options](#input\_cognito\_options) | Configuration block for authenticating Kibana with Cognito | `any` | `{}` | no | 201 | | [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no | 202 | | [create\_access\_policy](#input\_create\_access\_policy) | Determines whether an access policy will be created | `bool` | `true` | no | 203 | | [create\_cloudwatch\_log\_groups](#input\_create\_cloudwatch\_log\_groups) | Determines whether log groups are created | `bool` | `true` | no | 204 | | [create\_cloudwatch\_log\_resource\_policy](#input\_create\_cloudwatch\_log\_resource\_policy) | Determines whether a resource policy will be created for OpenSearch to log to CloudWatch | `bool` | `true` | no | 205 | | [create\_saml\_options](#input\_create\_saml\_options) | Determines whether SAML options will be created | `bool` | `false` | no | 206 | | [create\_security\_group](#input\_create\_security\_group) | Determines if a security group is created | `bool` | `true` | no | 207 | | [domain\_endpoint\_options](#input\_domain\_endpoint\_options) | Configuration block for domain endpoint HTTP(S) related options | `any` |
{
"enforce_https": true,
"tls_security_policy": "Policy-Min-TLS-1-2-2019-07"
}
| no | 208 | | [domain\_name](#input\_domain\_name) | Name of the domain | `string` | `""` | no | 209 | | [ebs\_options](#input\_ebs\_options) | Configuration block for EBS related options, may be required based on chosen [instance size](https://aws.amazon.com/elasticsearch-service/pricing/) | `any` |
{
"ebs_enabled": true,
"volume_size": 64,
"volume_type": "gp3"
}
| no | 210 | | [enable\_access\_policy](#input\_enable\_access\_policy) | Determines whether an access policy will be applied to the domain | `bool` | `true` | no | 211 | | [encrypt\_at\_rest](#input\_encrypt\_at\_rest) | Configuration block for encrypting at rest | `any` |
{
"enabled": true
}
| no | 212 | | [engine\_version](#input\_engine\_version) | Version of the OpenSearch engine to use. Must follow format 'OpenSearch\_X.Y' (e.g., 'OpenSearch\_2.11') | `string` | `null` | no | 213 | | [identity\_center\_options](#input\_identity\_center\_options) | Configuration block for enabling and managing IAM Identity Center integration within a domain |
object({
enabled_api_access = optional(bool)
identity_center_instance_arn = optional(string)
roles_key = optional(string)
subject_key = optional(string)
})
| `null` | no | 214 | | [ip\_address\_type](#input\_ip\_address\_type) | The IP address type for the endpoint. Valid values are ipv4 and dualstack | `string` | `null` | no | 215 | | [log\_publishing\_options](#input\_log\_publishing\_options) | Configuration block for publishing slow and application logs to CloudWatch Logs. This block can be declared multiple times, for each log\_type, within the same resource | `any` |
[
{
"log_type": "INDEX_SLOW_LOGS"
},
{
"log_type": "SEARCH_SLOW_LOGS"
}
]
| no | 216 | | [node\_to\_node\_encryption](#input\_node\_to\_node\_encryption) | Configuration block for node-to-node encryption options | `any` |
{
"enabled": true
}
| no | 217 | | [off\_peak\_window\_options](#input\_off\_peak\_window\_options) | Configuration to add Off Peak update options | `any` |
{
"enabled": true,
"off_peak_window": {
"hours": 7
}
}
| no | 218 | | [outbound\_connections](#input\_outbound\_connections) | Map of AWS OpenSearch outbound connections to create | `any` | `{}` | no | 219 | | [package\_associations](#input\_package\_associations) | Map of package association IDs to associate with the domain | `map(string)` | `{}` | no | 220 | | [region](#input\_region) | Region where this resource will be managed. Defaults to the Region set in the provider configuration | `string` | `null` | no | 221 | | [saml\_options](#input\_saml\_options) | SAML authentication options for an AWS OpenSearch Domain | `any` | `{}` | no | 222 | | [security\_group\_description](#input\_security\_group\_description) | Description of the security group created | `string` | `null` | no | 223 | | [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created | `string` | `null` | no | 224 | | [security\_group\_rules](#input\_security\_group\_rules) | Security group ingress and egress rules to add to the security group created | `any` | `{}` | no | 225 | | [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no | 226 | | [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no | 227 | | [software\_update\_options](#input\_software\_update\_options) | Software update options for the domain | `any` |
{
"auto_software_update_enabled": true
}
| no | 228 | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | 229 | | [timeouts](#input\_timeouts) | Create and delete timeout configurations for the domain | `map(string)` | `{}` | no | 230 | | [vpc\_endpoints](#input\_vpc\_endpoints) | Map of VPC endpoints to create for the domain | `any` | `{}` | no | 231 | | [vpc\_options](#input\_vpc\_options) | Configuration block for VPC related options. Adding or removing this configuration forces a new resource ([documentation](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-vpc.html#es-vpc-limitations)) | `any` | `{}` | no | 232 | 233 | ## Outputs 234 | 235 | | Name | Description | 236 | |------|-------------| 237 | | [cloudwatch\_logs](#output\_cloudwatch\_logs) | Map of CloudWatch log groups created and their attributes | 238 | | [domain\_arn](#output\_domain\_arn) | The Amazon Resource Name (ARN) of the domain | 239 | | [domain\_dashboard\_endpoint](#output\_domain\_dashboard\_endpoint) | Domain-specific endpoint for Dashboard without https scheme | 240 | | [domain\_dashboard\_endpoint\_v2](#output\_domain\_dashboard\_endpoint\_v2) | V2 domain endpoint for Dashboard that works with both IPv4 and IPv6 addresses, without https scheme | 241 | | [domain\_endpoint](#output\_domain\_endpoint) | Domain-specific endpoint used to submit index, search, and data upload requests | 242 | | [domain\_endpoint\_v2](#output\_domain\_endpoint\_v2) | V2 domain endpoint that works with both IPv4 and IPv6 addresses, used to submit index, search, and data upload requests | 243 | | [domain\_endpoint\_v2\_hosted\_zone\_id](#output\_domain\_endpoint\_v2\_hosted\_zone\_id) | Dual stack hosted zone ID for the domain. | 244 | | [domain\_id](#output\_domain\_id) | The unique identifier for the domain | 245 | | [domain\_name](#output\_domain\_name) | The name of the domain | 246 | | [outbound\_connections](#output\_outbound\_connections) | Map of outbound connections created and their attributes | 247 | | [package\_associations](#output\_package\_associations) | Map of package associations created and their attributes | 248 | | [security\_group\_arn](#output\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group | 249 | | [security\_group\_id](#output\_security\_group\_id) | ID of the security group | 250 | | [vpc\_endpoints](#output\_vpc\_endpoints) | Map of VPC endpoints created and their attributes | 251 | 252 | 253 | ## License 254 | 255 | Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-opensearch/blob/master/LICENSE). 256 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | data "aws_region" "current" { 2 | count = var.create ? 1 : 0 3 | 4 | region = var.region 5 | } 6 | data "aws_partition" "current" { 7 | count = var.create ? 1 : 0 8 | } 9 | data "aws_caller_identity" "current" { 10 | count = var.create ? 1 : 0 11 | } 12 | data "aws_iam_session_context" "current" { 13 | count = var.create ? 1 : 0 14 | arn = data.aws_caller_identity.current[0].arn 15 | } 16 | 17 | locals { 18 | account_id = try(data.aws_caller_identity.current[0].account_id, "") 19 | partition = try(data.aws_partition.current[0].partition, "") 20 | region = try(data.aws_region.current[0].region, "") 21 | 22 | static_domain_arn = "arn:${local.partition}:es:${local.region}:${local.account_id}:domain/${var.domain_name}" 23 | 24 | tags = merge(var.tags, { terraform-aws-modules = "opensearch" }) 25 | } 26 | 27 | ################################################################################ 28 | # Domain 29 | ################################################################################ 30 | 31 | resource "aws_opensearch_domain" "this" { 32 | count = var.create ? 1 : 0 33 | 34 | # Controlled via aws_opensearch_domain_policy below 35 | # access_policies = var.access_policies 36 | advanced_options = var.advanced_options 37 | 38 | dynamic "advanced_security_options" { 39 | for_each = length(var.advanced_security_options) > 0 ? [var.advanced_security_options] : [] 40 | 41 | content { 42 | anonymous_auth_enabled = try(advanced_security_options.value.anonymous_auth_enabled, false) 43 | enabled = try(advanced_security_options.value.enabled, true) 44 | internal_user_database_enabled = try(advanced_security_options.value.internal_user_database_enabled, null) 45 | 46 | dynamic "master_user_options" { 47 | for_each = try([advanced_security_options.value.master_user_options], [{}]) 48 | 49 | content { 50 | master_user_arn = try(master_user_options.value.master_user_arn, null) != null ? try(master_user_options.value.master_user_arn, data.aws_iam_session_context.current[0].issuer_arn) : null 51 | master_user_name = try(master_user_options.value.master_user_arn, null) == null ? try(master_user_options.value.master_user_name, null) : null 52 | master_user_password = try(master_user_options.value.master_user_arn, null) == null ? try(master_user_options.value.master_user_password, null) : null 53 | } 54 | } 55 | } 56 | } 57 | 58 | dynamic "aiml_options" { 59 | for_each = var.aiml_options != null ? [var.aiml_options] : [] 60 | content { 61 | 62 | dynamic "natural_language_query_generation_options" { 63 | for_each = aiml_options.value.natural_language_query_generation_options != null ? [aiml_options.value.natural_language_query_generation_options] : [] 64 | 65 | content { 66 | desired_state = natural_language_query_generation_options.value.desired_state 67 | } 68 | } 69 | 70 | dynamic "s3_vectors_engine" { 71 | for_each = aiml_options.value.s3_vectors_engine != null ? [aiml_options.value.s3_vectors_engine] : [] 72 | 73 | content { 74 | enabled = s3_vectors_engine.value.enabled 75 | } 76 | } 77 | } 78 | } 79 | 80 | dynamic "auto_tune_options" { 81 | for_each = length(var.auto_tune_options) > 0 ? [var.auto_tune_options] : [] 82 | 83 | content { 84 | desired_state = try(auto_tune_options.value.desired_state, "ENABLED") 85 | 86 | dynamic "maintenance_schedule" { 87 | for_each = try(auto_tune_options.value.maintenance_schedule, []) 88 | 89 | content { 90 | cron_expression_for_recurrence = maintenance_schedule.value.cron_expression_for_recurrence 91 | 92 | dynamic "duration" { 93 | for_each = [maintenance_schedule.value.duration] 94 | 95 | content { 96 | unit = duration.value.unit 97 | value = duration.value.value 98 | } 99 | } 100 | 101 | start_at = maintenance_schedule.value.start_at 102 | } 103 | } 104 | 105 | rollback_on_disable = try(auto_tune_options.value.rollback_on_disable, null) 106 | use_off_peak_window = try(auto_tune_options.value.use_off_peak_window, null) 107 | } 108 | } 109 | 110 | dynamic "cluster_config" { 111 | for_each = length(var.cluster_config) > 0 ? [var.cluster_config] : [] 112 | 113 | content { 114 | dynamic "cold_storage_options" { 115 | for_each = try([cluster_config.value.cold_storage_options], []) 116 | 117 | content { 118 | enabled = try(cold_storage_options.value.enabled, null) 119 | } 120 | } 121 | 122 | dedicated_master_count = try(cluster_config.value.dedicated_master_count, 3) 123 | dedicated_master_enabled = try(cluster_config.value.dedicated_master_enabled, true) 124 | dedicated_master_type = try(cluster_config.value.dedicated_master_type, "c6g.large.search") 125 | instance_count = try(cluster_config.value.instance_count, 3) 126 | instance_type = try(cluster_config.value.instance_type, "r6g.large.search") 127 | multi_az_with_standby_enabled = try(cluster_config.value.multi_az_with_standby_enabled, null) 128 | warm_count = try(cluster_config.value.warm_count, null) 129 | warm_enabled = try(cluster_config.value.warm_enabled, null) 130 | warm_type = try(cluster_config.value.warm_type, null) 131 | 132 | dynamic "node_options" { 133 | for_each = try(cluster_config.value.node_options, []) 134 | 135 | content { 136 | 137 | dynamic "node_config" { 138 | for_each = try([node_options.value.node_config], []) 139 | 140 | content { 141 | count = try(node_config.value.count, null) 142 | enabled = try(node_config.value.enabled, true) 143 | type = try(node_config.value.type, null) 144 | } 145 | } 146 | 147 | node_type = try(node_options.value.node_type, node_options.key, null) 148 | } 149 | } 150 | 151 | dynamic "zone_awareness_config" { 152 | for_each = try(cluster_config.value.zone_awareness_enabled, true) ? try([cluster_config.value.zone_awareness_config], []) : [] 153 | 154 | content { 155 | availability_zone_count = try(zone_awareness_config.value.availability_zone_count, null) 156 | } 157 | } 158 | 159 | zone_awareness_enabled = try(cluster_config.value.zone_awareness_enabled, true) 160 | } 161 | } 162 | 163 | dynamic "cognito_options" { 164 | for_each = length(var.cognito_options) > 0 ? [var.cognito_options] : [] 165 | 166 | content { 167 | enabled = try(cognito_options.value.enabled, null) 168 | identity_pool_id = cognito_options.value.identity_pool_id 169 | role_arn = cognito_options.value.role_arn 170 | user_pool_id = cognito_options.value.user_pool_id 171 | } 172 | } 173 | 174 | dynamic "domain_endpoint_options" { 175 | for_each = length(var.domain_endpoint_options) > 0 ? [var.domain_endpoint_options] : [] 176 | 177 | content { 178 | custom_endpoint = try(domain_endpoint_options.value.custom_endpoint, null) 179 | custom_endpoint_certificate_arn = try(domain_endpoint_options.value.custom_endpoint_certificate_arn, null) 180 | custom_endpoint_enabled = try(domain_endpoint_options.value.custom_endpoint_enabled, null) 181 | enforce_https = try(domain_endpoint_options.value.enforce_https, true) 182 | tls_security_policy = try(domain_endpoint_options.value.tls_security_policy, "Policy-Min-TLS-1-2-2019-07") 183 | } 184 | } 185 | 186 | domain_name = var.domain_name 187 | 188 | dynamic "ebs_options" { 189 | for_each = length(var.ebs_options) > 0 ? [var.ebs_options] : [] 190 | 191 | content { 192 | ebs_enabled = try(ebs_options.value.ebs_enabled, true) 193 | iops = try(ebs_options.value.iops, null) 194 | throughput = try(ebs_options.value.throughput, null) 195 | volume_size = try(ebs_options.value.volume_size, null) 196 | volume_type = try(ebs_options.value.volume_type, "gp3") 197 | } 198 | } 199 | 200 | dynamic "encrypt_at_rest" { 201 | for_each = length(var.encrypt_at_rest) > 0 ? [var.encrypt_at_rest] : [] 202 | 203 | content { 204 | enabled = try(encrypt_at_rest.value.enabled, true) 205 | kms_key_id = try(encrypt_at_rest.value.kms_key_id, null) 206 | } 207 | } 208 | 209 | engine_version = var.engine_version 210 | ip_address_type = var.ip_address_type 211 | 212 | dynamic "identity_center_options" { 213 | for_each = var.identity_center_options != null ? [var.identity_center_options] : [] 214 | 215 | content { 216 | enabled_api_access = identity_center_options.value.enabled_api_access 217 | identity_center_instance_arn = identity_center_options.value.identity_center_instance_arn 218 | roles_key = identity_center_options.value.roles_key 219 | subject_key = identity_center_options.value.subject_key 220 | } 221 | } 222 | 223 | dynamic "log_publishing_options" { 224 | for_each = { for opt in var.log_publishing_options : opt.log_type => opt } 225 | 226 | content { 227 | cloudwatch_log_group_arn = try(log_publishing_options.value.cloudwatch_log_group_arn, aws_cloudwatch_log_group.this[log_publishing_options.key].arn) 228 | enabled = try(log_publishing_options.value.enabled, true) 229 | log_type = log_publishing_options.value.log_type 230 | } 231 | } 232 | 233 | dynamic "node_to_node_encryption" { 234 | for_each = length(var.node_to_node_encryption) > 0 ? [var.node_to_node_encryption] : [] 235 | 236 | content { 237 | enabled = try(node_to_node_encryption.value.enabled, true) 238 | } 239 | } 240 | 241 | dynamic "off_peak_window_options" { 242 | for_each = length(var.off_peak_window_options) > 0 ? [var.off_peak_window_options] : [] 243 | 244 | content { 245 | enabled = try(off_peak_window_options.value.enabled, true) 246 | 247 | dynamic "off_peak_window" { 248 | for_each = try([off_peak_window_options.value.off_peak_window], []) 249 | 250 | content { 251 | dynamic "window_start_time" { 252 | for_each = try([off_peak_window.value.window_start_time], []) 253 | 254 | content { 255 | hours = try(window_start_time.value.hours, null) 256 | minutes = try(window_start_time.value.minutes, null) 257 | } 258 | } 259 | } 260 | } 261 | } 262 | } 263 | 264 | region = var.region 265 | 266 | dynamic "software_update_options" { 267 | for_each = length(var.software_update_options) > 0 ? [var.software_update_options] : [] 268 | 269 | content { 270 | auto_software_update_enabled = try(software_update_options.value.auto_software_update_enabled, true) 271 | } 272 | } 273 | 274 | dynamic "vpc_options" { 275 | for_each = length(var.vpc_options) > 0 ? [var.vpc_options] : [] 276 | 277 | content { 278 | security_group_ids = concat(try(vpc_options.value.security_group_ids, []), aws_security_group.this[*].id) 279 | subnet_ids = try(vpc_options.value.subnet_ids, null) 280 | } 281 | } 282 | 283 | timeouts { 284 | create = try(var.timeouts.create, null) 285 | delete = try(var.timeouts.delete, null) 286 | } 287 | 288 | tags = local.tags 289 | } 290 | 291 | ################################################################################ 292 | # Package Association(s) 293 | ################################################################################ 294 | 295 | resource "aws_opensearch_package_association" "this" { 296 | for_each = { for k, v in var.package_associations : k => v if var.create } 297 | 298 | package_id = try(each.value.package_id, each.key) 299 | domain_name = aws_opensearch_domain.this[0].domain_name 300 | region = try(each.value.region, var.region) 301 | } 302 | 303 | ################################################################################ 304 | # VPC Endpoint(s) 305 | ################################################################################ 306 | 307 | resource "aws_opensearch_vpc_endpoint" "this" { 308 | for_each = { for k, v in var.vpc_endpoints : k => v if var.create } 309 | 310 | domain_arn = aws_opensearch_domain.this[0].arn 311 | region = try(each.value.region, var.region) 312 | 313 | vpc_options { 314 | security_group_ids = try(each.value.security_group_ids, null) 315 | subnet_ids = each.value.subnet_ids 316 | } 317 | } 318 | 319 | ################################################################################ 320 | # Access Policy 321 | ################################################################################ 322 | 323 | locals { 324 | create_access_policy = var.create && var.create_access_policy && (length(var.access_policy_statements) > 0 || length(var.access_policy_source_policy_documents) > 0 || length(var.access_policy_override_policy_documents) > 0) 325 | } 326 | 327 | resource "aws_opensearch_domain_policy" "this" { 328 | count = var.create && var.enable_access_policy && (local.create_access_policy || var.access_policies != null) ? 1 : 0 329 | 330 | domain_name = aws_opensearch_domain.this[0].domain_name 331 | access_policies = local.create_access_policy ? data.aws_iam_policy_document.this[0].json : var.access_policies 332 | region = var.region 333 | } 334 | 335 | data "aws_iam_policy_document" "this" { 336 | count = local.create_access_policy ? 1 : 0 337 | 338 | source_policy_documents = var.access_policy_source_policy_documents 339 | override_policy_documents = var.access_policy_override_policy_documents 340 | 341 | dynamic "statement" { 342 | for_each = var.access_policy_statements 343 | 344 | content { 345 | sid = try(statement.value.sid, null) 346 | actions = try(statement.value.actions, null) 347 | not_actions = try(statement.value.not_actions, null) 348 | effect = try(statement.value.effect, null) 349 | resources = try(statement.value.resources, 350 | [for path in try(statement.value.resource_paths, ["*"]) : "${aws_opensearch_domain.this[0].arn}/${path}"] 351 | ) 352 | not_resources = try(statement.value.not_resources, null) 353 | 354 | dynamic "principals" { 355 | for_each = try(statement.value.principals, []) 356 | 357 | content { 358 | type = principals.value.type 359 | identifiers = principals.value.identifiers 360 | } 361 | } 362 | 363 | dynamic "not_principals" { 364 | for_each = try(statement.value.not_principals, []) 365 | 366 | content { 367 | type = not_principals.value.type 368 | identifiers = not_principals.value.identifiers 369 | } 370 | } 371 | 372 | dynamic "condition" { 373 | for_each = try(statement.value.conditions, []) 374 | 375 | content { 376 | test = condition.value.test 377 | values = condition.value.values 378 | variable = condition.value.variable 379 | } 380 | } 381 | } 382 | } 383 | } 384 | 385 | ################################################################################ 386 | # SAML Options 387 | ################################################################################ 388 | 389 | resource "aws_opensearch_domain_saml_options" "this" { 390 | count = var.create && var.create_saml_options ? 1 : 0 391 | 392 | domain_name = aws_opensearch_domain.this[0].domain_name 393 | region = var.region 394 | 395 | dynamic "saml_options" { 396 | for_each = length(var.saml_options) > 0 ? [var.saml_options] : [] 397 | 398 | content { 399 | enabled = try(saml_options.value.enabled, null) 400 | 401 | dynamic "idp" { 402 | for_each = try([saml_options.value.idp], []) 403 | 404 | content { 405 | entity_id = idp.value.entity_id 406 | metadata_content = idp.value.metadata_content 407 | } 408 | } 409 | 410 | master_backend_role = try(saml_options.value.master_backend_role, null) 411 | master_user_name = try(saml_options.value.master_user_name, null) 412 | roles_key = try(saml_options.value.roles_key, null) 413 | session_timeout_minutes = try(saml_options.value.session_timeout_minutes, null) 414 | subject_key = try(saml_options.value.subject_key, null) 415 | } 416 | } 417 | } 418 | 419 | ################################################################################ 420 | # Outbound Connections 421 | ################################################################################ 422 | 423 | resource "aws_opensearch_outbound_connection" "this" { 424 | for_each = { for k, v in var.outbound_connections : k => v if var.create } 425 | 426 | accept_connection = try(each.value.accept_connection, null) 427 | connection_alias = try(each.value.connection_alias, each.key) 428 | connection_mode = each.value.connection_mode 429 | region = try(each.value.region, var.region) 430 | 431 | dynamic "connection_properties" { 432 | for_each = try([each.value.connection_properties], []) 433 | 434 | content { 435 | dynamic "cross_cluster_search" { 436 | for_each = try([connection_properties.value.cross_cluster_search], []) 437 | 438 | content { 439 | skip_unavailable = try(cross_cluster_search.value.skip_unavailable, null) 440 | } 441 | } 442 | } 443 | } 444 | 445 | local_domain_info { 446 | owner_id = try(each.value.local_domain_info.owner_id, local.account_id) 447 | region = try(each.value.local_domain_info.region, local.region) 448 | domain_name = try(each.value.local_domain_info.domain_name, aws_opensearch_domain.this[0].domain_name) 449 | } 450 | 451 | remote_domain_info { 452 | owner_id = each.value.remote_domain_info.owner_id 453 | region = each.value.remote_domain_info.region 454 | domain_name = each.value.remote_domain_info.domain_name 455 | } 456 | } 457 | 458 | ################################################################################ 459 | # Cloudwatch Log Group 460 | ################################################################################ 461 | 462 | locals { 463 | create_cloudwatch_log_groups = var.create && var.create_cloudwatch_log_groups 464 | } 465 | 466 | resource "aws_cloudwatch_log_group" "this" { 467 | for_each = { for opt in var.log_publishing_options : opt.log_type => opt if try(opt.enabled, true) && local.create_cloudwatch_log_groups } 468 | 469 | name = try(each.value.log_group_name, "/aws/opensearch/${var.domain_name}/${each.key}") 470 | retention_in_days = try(each.value.log_group_retention_in_days, var.cloudwatch_log_group_retention_in_days) 471 | kms_key_id = try(each.value.log_group_kms_key_id, var.cloudwatch_log_group_kms_key_id) 472 | skip_destroy = try(each.value.log_group_skip_destroy, var.cloudwatch_log_group_skip_destroy) 473 | log_group_class = try(each.value.log_group_class, var.cloudwatch_log_group_class) 474 | region = try(each.value.region, var.region) 475 | 476 | tags = merge(local.tags, try(each.value.log_group_tags, {})) 477 | } 478 | 479 | data "aws_iam_policy_document" "cloudwatch" { 480 | count = local.create_cloudwatch_log_groups && var.create_cloudwatch_log_resource_policy ? 1 : 0 481 | 482 | statement { 483 | actions = [ 484 | "logs:CreateLogStream", 485 | "logs:PutLogEvents", 486 | "logs:PutLogEventsBatch", 487 | ] 488 | 489 | # https://github.com/hashicorp/terraform-provider-aws/issues/14497 490 | # resources = coalescelist([for log in aws_cloudwatch_log_group.this : "${log.arn}:*"], ["arn:${local.partition}:logs:*"]) 491 | resources = ["arn:${local.partition}:logs:*"] 492 | 493 | principals { 494 | identifiers = ["es.amazonaws.com"] 495 | type = "Service" 496 | } 497 | 498 | condition { 499 | test = "StringEquals" 500 | variable = "aws:SourceAccount" 501 | values = [local.account_id] 502 | } 503 | 504 | condition { 505 | test = "ArnLike" 506 | variable = "aws:SourceArn" 507 | values = [local.static_domain_arn] 508 | } 509 | } 510 | } 511 | 512 | resource "aws_cloudwatch_log_resource_policy" "this" { 513 | count = local.create_cloudwatch_log_groups && var.create_cloudwatch_log_resource_policy ? 1 : 0 514 | 515 | policy_document = data.aws_iam_policy_document.cloudwatch[0].json 516 | policy_name = coalesce(var.cloudwatch_log_resource_policy_name, "opensearch-${var.domain_name}") 517 | region = var.region 518 | } 519 | 520 | ################################################################################ 521 | # Security Group 522 | ################################################################################ 523 | 524 | locals { 525 | create_security_group = var.create && var.create_security_group && length(var.vpc_options) > 0 526 | security_group_name = try(coalesce(var.security_group_name, var.domain_name), "") 527 | } 528 | 529 | data "aws_subnet" "this" { 530 | count = local.create_security_group ? 1 : 0 531 | 532 | region = var.region 533 | id = element(var.vpc_options.subnet_ids, 0) 534 | } 535 | 536 | resource "aws_security_group" "this" { 537 | count = local.create_security_group ? 1 : 0 538 | 539 | name = var.security_group_use_name_prefix ? null : local.security_group_name 540 | name_prefix = var.security_group_use_name_prefix ? "${local.security_group_name}-" : null 541 | description = var.security_group_description 542 | vpc_id = data.aws_subnet.this[0].vpc_id 543 | region = var.region 544 | revoke_rules_on_delete = true 545 | 546 | tags = merge(local.tags, var.security_group_tags) 547 | 548 | lifecycle { 549 | create_before_destroy = true 550 | } 551 | } 552 | 553 | resource "aws_vpc_security_group_ingress_rule" "this" { 554 | for_each = { for k, v in var.security_group_rules : k => v if local.create_security_group && try(v.type, "ingress") == "ingress" } 555 | 556 | # Required 557 | security_group_id = aws_security_group.this[0].id 558 | ip_protocol = try(each.value.ip_protocol, "tcp") 559 | 560 | # Optional 561 | cidr_ipv4 = lookup(each.value, "cidr_ipv4", null) 562 | cidr_ipv6 = lookup(each.value, "cidr_ipv6", null) 563 | description = try(each.value.description, null) 564 | from_port = try(each.value.from_port, 443) 565 | prefix_list_id = lookup(each.value, "prefix_list_id", null) 566 | referenced_security_group_id = lookup(each.value, "referenced_security_group_id", null) 567 | to_port = try(each.value.to_port, 443) 568 | region = try(each.value.region, var.region) 569 | 570 | tags = merge(local.tags, var.security_group_tags, try(each.value.tags, {})) 571 | } 572 | 573 | resource "aws_vpc_security_group_egress_rule" "this" { 574 | for_each = { for k, v in var.security_group_rules : k => v if local.create_security_group && try(v.type, "ingress") == "egress" } 575 | 576 | # Required 577 | security_group_id = aws_security_group.this[0].id 578 | ip_protocol = try(each.value.ip_protocol, "tcp") 579 | 580 | # Optional 581 | cidr_ipv4 = lookup(each.value, "cidr_ipv4", null) 582 | cidr_ipv6 = lookup(each.value, "cidr_ipv6", null) 583 | description = try(each.value.description, null) 584 | from_port = try(each.value.from_port, null) 585 | prefix_list_id = lookup(each.value, "prefix_list_id", null) 586 | referenced_security_group_id = lookup(each.value, "referenced_security_group_id", null) 587 | to_port = try(each.value.to_port, null) 588 | region = try(each.value.region, var.region) 589 | 590 | tags = merge(local.tags, var.security_group_tags, try(each.value.tags, {})) 591 | } 592 | --------------------------------------------------------------------------------