diff --git a/README.md b/README.md index 66d27b3..80482b8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,8 @@ - -# terraform-aws-elasticsearch [![Codefresh Build Status](https://g.codefresh.io/api/badges/pipeline/cloudposse/terraform-modules%2Fterraform-aws-elasticsearch?type=cf-1)](https://g.codefresh.io/public/accounts/cloudposse/pipelines/5d22bfe5a7e22ea3b67ea820) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-elasticsearch.svg)](https://github.com/cloudposse/terraform-aws-elasticsearch/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) +# terraform-aws-elasticsearch +Codefresh Build StatusLatest ReleaseSlack Community -[![README Header][readme_header_img]][readme_header_link] - -[![Cloud Posse][logo]](https://cpco.io/homepage) - -| [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]
[Andriy Knysh][aknysh_homepage] | [![Igor Rodionov][goruha_avatar]][goruha_homepage]
[Igor Rodionov][goruha_homepage] | [![Sarkis Varozian][sarkis_avatar]][sarkis_homepage]
[Sarkis Varozian][sarkis_homepage] | [![Marcin Brański][3h4x_avatar]][3h4x_homepage]
[Marcin Brański][3h4x_homepage] | -|---|---|---|---|---| - +README footer - [osterman_homepage]: https://github.com/osterman - [osterman_avatar]: https://img.cloudposse.com/150x150/https://github.com/osterman.png - [aknysh_homepage]: https://github.com/aknysh - [aknysh_avatar]: https://img.cloudposse.com/150x150/https://github.com/aknysh.png - [goruha_homepage]: https://github.com/goruha - [goruha_avatar]: https://img.cloudposse.com/150x150/https://github.com/goruha.png - [sarkis_homepage]: https://github.com/sarkis - [sarkis_avatar]: https://img.cloudposse.com/150x150/https://github.com/sarkis.png - [3h4x_homepage]: https://github.com/3h4x - [3h4x_avatar]: https://img.cloudposse.com/150x150/https://github.com/3h4x.png - -[![README Footer][readme_footer_img]][readme_footer_link] -[![Beacon][beacon]][website] - - [logo]: https://cloudposse.com/logo-300x69.svg - [docs]: https://cpco.io/docs?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=docs - [website]: https://cpco.io/homepage?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=website - [github]: https://cpco.io/github?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=github - [jobs]: https://cpco.io/jobs?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=jobs - [hire]: https://cpco.io/hire?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=hire - [slack]: https://cpco.io/slack?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=slack - [linkedin]: https://cpco.io/linkedin?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=linkedin - [twitter]: https://cpco.io/twitter?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=twitter - [testimonial]: https://cpco.io/leave-testimonial?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=testimonial - [office_hours]: https://cloudposse.com/office-hours?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=office_hours - [newsletter]: https://cpco.io/newsletter?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=newsletter - [discourse]: https://ask.sweetops.com/?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=discourse - [email]: https://cpco.io/email?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=email - [commercial_support]: https://cpco.io/commercial-support?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=commercial_support - [we_love_open_source]: https://cpco.io/we-love-open-source?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=we_love_open_source - [terraform_modules]: https://cpco.io/terraform-modules?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=terraform_modules - [readme_header_img]: https://cloudposse.com/readme/header/img - [readme_header_link]: https://cloudposse.com/readme/header/link?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=readme_header_link - [readme_footer_img]: https://cloudposse.com/readme/footer/img - [readme_footer_link]: https://cloudposse.com/readme/footer/link?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=readme_footer_link - [readme_commercial_support_img]: https://cloudposse.com/readme/commercial-support/img - [readme_commercial_support_link]: https://cloudposse.com/readme/commercial-support/link?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/terraform-aws-elasticsearch&utm_content=readme_commercial_support_link - [share_twitter]: https://twitter.com/intent/tweet/?text=terraform-aws-elasticsearch&url=https://github.com/cloudposse/terraform-aws-elasticsearch - [share_linkedin]: https://www.linkedin.com/shareArticle?mini=true&title=terraform-aws-elasticsearch&url=https://github.com/cloudposse/terraform-aws-elasticsearch - [share_reddit]: https://reddit.com/submit/?url=https://github.com/cloudposse/terraform-aws-elasticsearch - [share_facebook]: https://facebook.com/sharer/sharer.php?u=https://github.com/cloudposse/terraform-aws-elasticsearch - [share_googleplus]: https://plus.google.com/share?url=https://github.com/cloudposse/terraform-aws-elasticsearch - [share_email]: mailto:?subject=terraform-aws-elasticsearch&body=https://github.com/cloudposse/terraform-aws-elasticsearch - [beacon]: https://ga-beacon.cloudposse.com/UA-76589703-4/cloudposse/terraform-aws-elasticsearch?pixel&cs=github&cm=readme&an=terraform-aws-elasticsearch - +Beacon diff --git a/docs/terraform.md b/docs/terraform.md index 6df0fdc..57ea59c 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -5,7 +5,6 @@ |------|---------| | [terraform](#requirement\_terraform) | >= 1.3 | | [aws](#requirement\_aws) | >= 3.35.0 | -| [null](#requirement\_null) | >= 2.0 | ## Providers @@ -31,6 +30,8 @@ | [aws_elasticsearch_domain_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/elasticsearch_domain_policy) | resource | | [aws_iam_role.elasticsearch_user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_service_linked_role.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_service_linked_role) | resource | +| [aws_opensearch_domain.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain) | resource | +| [aws_opensearch_domain_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain_policy) | resource | | [aws_security_group.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_security_group_rule.egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.ingress_cidr_blocks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | @@ -50,11 +51,13 @@ | [advanced\_security\_options\_master\_user\_name](#input\_advanced\_security\_options\_master\_user\_name) | Master user username (applicable if advanced\_security\_options\_internal\_user\_database\_enabled set to true) | `string` | `""` | no | | [advanced\_security\_options\_master\_user\_password](#input\_advanced\_security\_options\_master\_user\_password) | Master user password (applicable if advanced\_security\_options\_internal\_user\_database\_enabled set to true) | `string` | `""` | no | | [allowed\_cidr\_blocks](#input\_allowed\_cidr\_blocks) | List of CIDR blocks to be allowed to connect to the cluster | `list(string)` | `[]` | no | +| [anonymous\_iam\_actions](#input\_anonymous\_iam\_actions) | List of actions to allow for the anonymous (`*`) IAM roles, _e.g._ `es:ESHttpGet`, `es:ESHttpPut`, `es:ESHttpPost` | `list(string)` | `[]` | no | | [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | | [auto\_tune](#input\_auto\_tune) | This object represents the auto\_tune configuration. It contains the following filed:
- enabled - Whether to enable autotune.
- rollback\_on\_disable - Whether to roll back to default Auto-Tune settings when disabling Auto-Tune.
- starting\_time - Date and time at which to start the Auto-Tune maintenance schedule in RFC3339 format. Time should be in the future.
- cron\_schedule - A cron expression specifying the recurrence pattern for an Auto-Tune maintenance schedule.
- duration - Autotune maintanance window duration time in hours. |
object({
enabled = bool
rollback_on_disable = string
starting_time = string
cron_schedule = string
duration = number
})
|
{
"cron_schedule": null,
"duration": null,
"enabled": false,
"rollback_on_disable": "NO_ROLLBACK",
"starting_time": null
}
| no | | [automated\_snapshot\_start\_hour](#input\_automated\_snapshot\_start\_hour) | Hour at which automated snapshots are taken, in UTC | `number` | `0` | no | | [availability\_zone\_count](#input\_availability\_zone\_count) | Number of Availability Zones for the domain to use. | `number` | `2` | no | | [aws\_ec2\_service\_name](#input\_aws\_ec2\_service\_name) | AWS EC2 Service Name | `list(string)` |
[
"ec2.amazonaws.com"
]
| no | +| [aws\_service\_type](#input\_aws\_service\_type) | The type of AWS service to deploy (`elasticsearch` or `opensearch`). | `string` | `"elasticsearch"` | no | | [cognito\_authentication\_enabled](#input\_cognito\_authentication\_enabled) | Whether to enable Amazon Cognito authentication with Kibana | `bool` | `false` | no | | [cognito\_iam\_role\_arn](#input\_cognito\_iam\_role\_arn) | ARN of the IAM role that has the AmazonESCognitoAccess policy attached | `string` | `""` | no | | [cognito\_identity\_pool\_id](#input\_cognito\_identity\_pool\_id) | The ID of the Cognito Identity Pool to use | `string` | `""` | no | @@ -86,7 +89,7 @@ | [encrypt\_at\_rest\_enabled](#input\_encrypt\_at\_rest\_enabled) | Whether to enable encryption at rest | `bool` | `true` | no | | [encrypt\_at\_rest\_kms\_key\_id](#input\_encrypt\_at\_rest\_kms\_key\_id) | The KMS key ID to encrypt the Elasticsearch domain with. If not specified, then it defaults to using the AWS/Elasticsearch service KMS key | `string` | `""` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | -| [iam\_actions](#input\_iam\_actions) | List of actions to allow for the IAM roles, _e.g._ `es:ESHttpGet`, `es:ESHttpPut`, `es:ESHttpPost` | `list(string)` | `[]` | no | +| [iam\_actions](#input\_iam\_actions) | List of actions to allow for the user IAM roles, _e.g._ `es:ESHttpGet`, `es:ESHttpPut`, `es:ESHttpPost` | `list(string)` | `[]` | no | | [iam\_authorizing\_role\_arns](#input\_iam\_authorizing\_role\_arns) | List of IAM role ARNs to permit to assume the Elasticsearch user role | `list(string)` | `[]` | no | | [iam\_role\_arns](#input\_iam\_role\_arns) | List of IAM role ARNs to permit access to the Elasticsearch domain | `list(string)` | `[]` | no | | [iam\_role\_max\_session\_duration](#input\_iam\_role\_max\_session\_duration) | The maximum session duration (in seconds) for the user role. Can have a value from 1 hour to 12 hours | `number` | `3600` | no | diff --git a/elasticsearch_domain.tf b/elasticsearch_domain.tf new file mode 100644 index 0000000..2d3071e --- /dev/null +++ b/elasticsearch_domain.tf @@ -0,0 +1,146 @@ +# +# Elasticsearch Domain +# + +resource "aws_elasticsearch_domain_policy" "default" { + count = local.elasticsearch_enabled && (length(var.iam_authorizing_role_arns) > 0 || length(var.iam_role_arns) > 0) ? 1 : 0 + domain_name = module.this.id + access_policies = join("", data.aws_iam_policy_document.default[*].json) +} + +resource "aws_elasticsearch_domain" "default" { + count = local.elasticsearch_enabled ? 1 : 0 + domain_name = module.this.id + elasticsearch_version = var.elasticsearch_version + + advanced_options = var.advanced_options + + advanced_security_options { + enabled = var.advanced_security_options_enabled + internal_user_database_enabled = var.advanced_security_options_internal_user_database_enabled + master_user_options { + master_user_arn = var.advanced_security_options_master_user_arn + master_user_name = var.advanced_security_options_master_user_name + master_user_password = var.advanced_security_options_master_user_password + } + } + + ebs_options { + ebs_enabled = var.ebs_volume_size > 0 ? true : false + volume_size = var.ebs_volume_size + volume_type = var.ebs_volume_type + iops = var.ebs_iops + throughput = var.ebs_throughput + } + + encrypt_at_rest { + enabled = var.encrypt_at_rest_enabled + kms_key_id = var.encrypt_at_rest_kms_key_id + } + + domain_endpoint_options { + enforce_https = var.domain_endpoint_options_enforce_https + tls_security_policy = var.domain_endpoint_options_tls_security_policy + custom_endpoint_enabled = var.custom_endpoint_enabled + custom_endpoint = var.custom_endpoint_enabled ? var.custom_endpoint : null + custom_endpoint_certificate_arn = var.custom_endpoint_enabled ? var.custom_endpoint_certificate_arn : null + } + + cluster_config { + instance_count = var.instance_count + instance_type = var.instance_type + dedicated_master_enabled = var.dedicated_master_enabled + dedicated_master_count = var.dedicated_master_enabled ? var.dedicated_master_count : null + dedicated_master_type = var.dedicated_master_enabled ? var.dedicated_master_type : null + zone_awareness_enabled = var.zone_awareness_enabled + warm_enabled = var.warm_enabled + warm_count = var.warm_enabled ? var.warm_count : null + warm_type = var.warm_enabled ? var.warm_type : null + + dynamic "zone_awareness_config" { + for_each = var.availability_zone_count > 1 && var.zone_awareness_enabled ? [true] : [] + content { + availability_zone_count = var.availability_zone_count + } + } + + dynamic "cold_storage_options" { + for_each = var.cold_storage_enabled ? [true] : [] + content { + enabled = var.cold_storage_enabled + } + } + } + + dynamic "auto_tune_options" { + for_each = var.auto_tune.enabled ? [true] : [] + content { + desired_state = "ENABLED" + rollback_on_disable = var.auto_tune.rollback_on_disable + maintenance_schedule { + # Required until https://github.com/hashicorp/terraform-provider-aws/issues/22239 would be resolved + start_at = var.auto_tune.starting_time == null ? timeadd(timestamp(), "1h") : var.auto_tune.starting_time + duration { + value = var.auto_tune.duration + unit = "HOURS" + } + cron_expression_for_recurrence = var.auto_tune.cron_schedule + } + } + } + + node_to_node_encryption { + enabled = var.node_to_node_encryption_enabled + } + + dynamic "vpc_options" { + for_each = var.vpc_enabled ? [true] : [] + + content { + security_group_ids = var.create_security_group ? [join("", aws_security_group.default[*].id)] : var.security_groups + subnet_ids = var.subnet_ids + } + } + + snapshot_options { + automated_snapshot_start_hour = var.automated_snapshot_start_hour + } + + dynamic "cognito_options" { + for_each = var.cognito_authentication_enabled ? [true] : [] + content { + enabled = true + user_pool_id = var.cognito_user_pool_id + identity_pool_id = var.cognito_identity_pool_id + role_arn = var.cognito_iam_role_arn + } + } + + log_publishing_options { + enabled = var.log_publishing_index_enabled + log_type = "INDEX_SLOW_LOGS" + cloudwatch_log_group_arn = var.log_publishing_index_cloudwatch_log_group_arn + } + + log_publishing_options { + enabled = var.log_publishing_search_enabled + log_type = "SEARCH_SLOW_LOGS" + cloudwatch_log_group_arn = var.log_publishing_search_cloudwatch_log_group_arn + } + + log_publishing_options { + enabled = var.log_publishing_audit_enabled + log_type = "AUDIT_LOGS" + cloudwatch_log_group_arn = var.log_publishing_audit_cloudwatch_log_group_arn + } + + log_publishing_options { + enabled = var.log_publishing_application_enabled + log_type = "ES_APPLICATION_LOGS" + cloudwatch_log_group_arn = var.log_publishing_application_cloudwatch_log_group_arn + } + + tags = module.this.tags + + depends_on = [aws_iam_service_linked_role.default] +} diff --git a/examples/opensearch_basic/main.tf b/examples/opensearch_basic/main.tf new file mode 100644 index 0000000..9bceee1 --- /dev/null +++ b/examples/opensearch_basic/main.tf @@ -0,0 +1,28 @@ +provider "aws" { + region = "us-east-2" +} + +module "opensearch" { + source = "../../" + namespace = "eg" + stage = "dev" + name = "es" + dns_zone_id = "Z14EN2YD427LRQ" + security_groups = ["sg-XXXXXXXXX", "sg-YYYYYYYY"] + vpc_id = "vpc-XXXXXXXXX" + subnet_ids = ["subnet-XXXXXXXXX", "subnet-YYYYYYYY"] + zone_awareness_enabled = "true" + aws_service_type = "opensearch" + elasticsearch_version = "OpenSearch_2.9" + instance_type = "t3.small.search" + instance_count = 4 + ebs_volume_size = 10 + iam_role_arns = ["arn:aws:iam::XXXXXXXXX:role/ops", "arn:aws:iam::XXXXXXXXX:role/dev"] + iam_actions = ["es:ESHttpGet", "es:ESHttpPut", "es:ESHttpPost"] + encrypt_at_rest_enabled = "true" + kibana_subdomain_name = "kibana-es" + + advanced_options = { + "rest.action.multi.allow_explicit_index" = "true" + } +} diff --git a/examples/opensearch_basic/versions.tf b/examples/opensearch_basic/versions.tf new file mode 100644 index 0000000..fe97db9 --- /dev/null +++ b/examples/opensearch_basic/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + } +} diff --git a/main.tf b/main.tf index ebf17c8..d0e193e 100644 --- a/main.tf +++ b/main.tf @@ -1,3 +1,16 @@ +locals { + elasticsearch_enabled = module.this.enabled && var.aws_service_type == "elasticsearch" ? true : false + opensearch_enabled = module.this.enabled && var.aws_service_type == "opensearch" ? true : false + + service_linked_role_name = local.elasticsearch_enabled ? "AWSServiceRoleForAmazonElasticsearchService" : "AWSServiceRoleForAmazonOpenSearchService" + + aws_service_domain_arn = coalesce(join("", aws_elasticsearch_domain.default[*].arn), join("", aws_opensearch_domain.default[*].arn)) + aws_service_domain_endpoint = coalesce(join("", aws_elasticsearch_domain.default[*].endpoint), join("", aws_opensearch_domain.default[*].endpoint)) + aws_service_domain_id = coalesce(join("", aws_elasticsearch_domain.default[*].domain_id), join("", aws_opensearch_domain.default[*].domain_id)) + aws_service_domain_name = coalesce(join("", aws_elasticsearch_domain.default[*].domain_name), join("", aws_opensearch_domain.default[*].domain_name)) + aws_service_domain_kibana_endpoint = coalesce(join("", aws_elasticsearch_domain.default[*].kibana_endpoint), join("", aws_opensearch_domain.default[*].kibana_endpoint)) +} + module "user_label" { source = "cloudposse/label/null" version = "0.25.0" @@ -65,7 +78,7 @@ resource "aws_security_group_rule" "egress" { resource "aws_iam_service_linked_role" "default" { count = module.this.enabled && var.create_iam_service_linked_role ? 1 : 0 aws_service_name = "es.amazonaws.com" - description = "AWSServiceRoleForAmazonElasticsearchService Service-Linked Role" + description = "${local.service_linked_role_name} Service-Linked Role" } # Role that pods can assume for access to elasticsearch and kibana @@ -103,154 +116,19 @@ data "aws_iam_policy_document" "assume_role" { } } -resource "aws_elasticsearch_domain" "default" { - count = module.this.enabled ? 1 : 0 - domain_name = module.this.id - elasticsearch_version = var.elasticsearch_version - - advanced_options = var.advanced_options - - advanced_security_options { - enabled = var.advanced_security_options_enabled - internal_user_database_enabled = var.advanced_security_options_internal_user_database_enabled - master_user_options { - master_user_arn = var.advanced_security_options_master_user_arn - master_user_name = var.advanced_security_options_master_user_name - master_user_password = var.advanced_security_options_master_user_password - } - } - - ebs_options { - ebs_enabled = var.ebs_volume_size > 0 ? true : false - volume_size = var.ebs_volume_size - volume_type = var.ebs_volume_type - iops = var.ebs_iops - throughput = var.ebs_throughput - } - - encrypt_at_rest { - enabled = var.encrypt_at_rest_enabled - kms_key_id = var.encrypt_at_rest_kms_key_id - } - - domain_endpoint_options { - enforce_https = var.domain_endpoint_options_enforce_https - tls_security_policy = var.domain_endpoint_options_tls_security_policy - custom_endpoint_enabled = var.custom_endpoint_enabled - custom_endpoint = var.custom_endpoint_enabled ? var.custom_endpoint : null - custom_endpoint_certificate_arn = var.custom_endpoint_enabled ? var.custom_endpoint_certificate_arn : null - } - - cluster_config { - instance_count = var.instance_count - instance_type = var.instance_type - dedicated_master_enabled = var.dedicated_master_enabled - dedicated_master_count = var.dedicated_master_enabled ? var.dedicated_master_count : null - dedicated_master_type = var.dedicated_master_enabled ? var.dedicated_master_type : null - zone_awareness_enabled = var.zone_awareness_enabled - warm_enabled = var.warm_enabled - warm_count = var.warm_enabled ? var.warm_count : null - warm_type = var.warm_enabled ? var.warm_type : null - - dynamic "zone_awareness_config" { - for_each = var.availability_zone_count > 1 && var.zone_awareness_enabled ? [true] : [] - content { - availability_zone_count = var.availability_zone_count - } - } - - dynamic "cold_storage_options" { - for_each = var.cold_storage_enabled ? [true] : [] - content { - enabled = var.cold_storage_enabled - } - } - } - - dynamic "auto_tune_options" { - for_each = var.auto_tune.enabled ? [true] : [] - content { - desired_state = "ENABLED" - rollback_on_disable = var.auto_tune.rollback_on_disable - maintenance_schedule { - # Required until https://github.com/hashicorp/terraform-provider-aws/issues/22239 would be resolved - start_at = var.auto_tune.starting_time == null ? timeadd(timestamp(), "1h") : var.auto_tune.starting_time - duration { - value = var.auto_tune.duration - unit = "HOURS" - } - cron_expression_for_recurrence = var.auto_tune.cron_schedule - } - } - } - - node_to_node_encryption { - enabled = var.node_to_node_encryption_enabled - } - - dynamic "vpc_options" { - for_each = var.vpc_enabled ? [true] : [] - - content { - security_group_ids = var.create_security_group ? [join("", aws_security_group.default[*].id)] : var.security_groups - subnet_ids = var.subnet_ids - } - } - - snapshot_options { - automated_snapshot_start_hour = var.automated_snapshot_start_hour - } - - dynamic "cognito_options" { - for_each = var.cognito_authentication_enabled ? [true] : [] - content { - enabled = true - user_pool_id = var.cognito_user_pool_id - identity_pool_id = var.cognito_identity_pool_id - role_arn = var.cognito_iam_role_arn - } - } - - log_publishing_options { - enabled = var.log_publishing_index_enabled - log_type = "INDEX_SLOW_LOGS" - cloudwatch_log_group_arn = var.log_publishing_index_cloudwatch_log_group_arn - } - - log_publishing_options { - enabled = var.log_publishing_search_enabled - log_type = "SEARCH_SLOW_LOGS" - cloudwatch_log_group_arn = var.log_publishing_search_cloudwatch_log_group_arn - } - - log_publishing_options { - enabled = var.log_publishing_audit_enabled - log_type = "AUDIT_LOGS" - cloudwatch_log_group_arn = var.log_publishing_audit_cloudwatch_log_group_arn - } - - log_publishing_options { - enabled = var.log_publishing_application_enabled - log_type = "ES_APPLICATION_LOGS" - cloudwatch_log_group_arn = var.log_publishing_application_cloudwatch_log_group_arn - } - - tags = module.this.tags - - depends_on = [aws_iam_service_linked_role.default] -} - data "aws_iam_policy_document" "default" { count = module.this.enabled && (length(var.iam_authorizing_role_arns) > 0 || length(var.iam_role_arns) > 0) ? 1 : 0 statement { + sid = "User" + effect = "Allow" actions = distinct(compact(var.iam_actions)) resources = [ - join("", aws_elasticsearch_domain.default[*].arn), - "${join("", aws_elasticsearch_domain.default[*].arn)}/*" + local.aws_service_domain_arn, + "${local.aws_service_domain_arn}/*" ] principals { @@ -265,13 +143,15 @@ data "aws_iam_policy_document" "default" { dynamic "statement" { for_each = length(var.allowed_cidr_blocks) > 0 && !var.vpc_enabled ? [true] : [] content { + sid = "Anonymous" + effect = "Allow" - actions = distinct(compact(var.iam_actions)) + actions = distinct(compact(var.anonymous_iam_actions)) resources = [ - join("", aws_elasticsearch_domain.default[*].arn), - "${join("", aws_elasticsearch_domain.default[*].arn)}/*" + local.aws_service_domain_arn, + "${local.aws_service_domain_arn}/*" ] principals { @@ -288,12 +168,6 @@ data "aws_iam_policy_document" "default" { } } -resource "aws_elasticsearch_domain_policy" "default" { - count = module.this.enabled && (length(var.iam_authorizing_role_arns) > 0 || length(var.iam_role_arns) > 0) ? 1 : 0 - domain_name = module.this.id - access_policies = join("", data.aws_iam_policy_document.default[*].json) -} - module "domain_hostname" { source = "cloudposse/route53-cluster-hostname/aws" version = "0.12.3" @@ -302,7 +176,7 @@ module "domain_hostname" { dns_name = var.elasticsearch_subdomain_name == "" ? module.this.id : var.elasticsearch_subdomain_name ttl = 60 zone_id = var.dns_zone_id - records = [join("", aws_elasticsearch_domain.default[*].endpoint)] + records = [local.aws_service_domain_endpoint] context = module.this.context } @@ -318,7 +192,7 @@ module "kibana_hostname" { # Note: kibana_endpoint is not just a domain name, it includes a path component, # and as such is not suitable for a DNS record. The plain endpoint is the # hostname portion and should be used for DNS. - records = [join("", aws_elasticsearch_domain.default[*].endpoint)] + records = [local.aws_service_domain_endpoint] context = module.this.context } diff --git a/opensearch_domain.tf b/opensearch_domain.tf new file mode 100644 index 0000000..ec4daa7 --- /dev/null +++ b/opensearch_domain.tf @@ -0,0 +1,136 @@ +# +# Opensearch Domain +# + +resource "aws_opensearch_domain_policy" "default" { + count = local.opensearch_enabled && (length(var.iam_authorizing_role_arns) > 0 || length(var.iam_role_arns) > 0) ? 1 : 0 + domain_name = module.this.id + access_policies = join("", data.aws_iam_policy_document.default[*].json) +} + +resource "aws_opensearch_domain" "default" { + count = local.opensearch_enabled ? 1 : 0 + domain_name = module.this.id + engine_version = var.elasticsearch_version + + advanced_options = var.advanced_options + + advanced_security_options { + enabled = var.advanced_security_options_enabled + internal_user_database_enabled = var.advanced_security_options_internal_user_database_enabled + master_user_options { + master_user_arn = var.advanced_security_options_master_user_arn + master_user_name = var.advanced_security_options_master_user_name + master_user_password = var.advanced_security_options_master_user_password + } + } + + ebs_options { + ebs_enabled = var.ebs_volume_size > 0 ? true : false + volume_size = var.ebs_volume_size + volume_type = var.ebs_volume_type + iops = var.ebs_iops + } + + encrypt_at_rest { + enabled = var.encrypt_at_rest_enabled + kms_key_id = var.encrypt_at_rest_kms_key_id + } + + domain_endpoint_options { + enforce_https = var.domain_endpoint_options_enforce_https + tls_security_policy = var.domain_endpoint_options_tls_security_policy + custom_endpoint_enabled = var.custom_endpoint_enabled + custom_endpoint = var.custom_endpoint_enabled ? var.custom_endpoint : null + custom_endpoint_certificate_arn = var.custom_endpoint_enabled ? var.custom_endpoint_certificate_arn : null + } + + cluster_config { + instance_count = var.instance_count + instance_type = var.instance_type + dedicated_master_enabled = var.dedicated_master_enabled + dedicated_master_count = var.dedicated_master_count + dedicated_master_type = var.dedicated_master_type + zone_awareness_enabled = var.zone_awareness_enabled + warm_enabled = var.warm_enabled + warm_count = var.warm_enabled ? var.warm_count : null + warm_type = var.warm_enabled ? var.warm_type : null + + dynamic "zone_awareness_config" { + for_each = var.availability_zone_count > 1 && var.zone_awareness_enabled ? [true] : [] + content { + availability_zone_count = var.availability_zone_count + } + } + } + + node_to_node_encryption { + enabled = var.node_to_node_encryption_enabled + } + + dynamic "vpc_options" { + for_each = var.vpc_enabled ? [true] : [] + + content { + security_group_ids = [join("", aws_security_group.default[*].id)] + subnet_ids = var.subnet_ids + } + } + + snapshot_options { + automated_snapshot_start_hour = var.automated_snapshot_start_hour + } + + dynamic "cognito_options" { + for_each = var.cognito_authentication_enabled ? [true] : [] + content { + enabled = true + user_pool_id = var.cognito_user_pool_id + identity_pool_id = var.cognito_identity_pool_id + role_arn = var.cognito_iam_role_arn + } + } + + # Converted `log_publishing_options` to dynamic blocks due to the follow errors + # │ Error: Error creating OpenSearch domain: InvalidParameter: 4 validation error(s) found. + # │ - minimum field size of 20, CreateDomainInput.LogPublishingOptions[ES_APPLICATION_LOGS].CloudWatchLogsLogGroupArn. + # │ - minimum field size of 20, CreateDomainInput.LogPublishingOptions[AUDIT_LOGS].CloudWatchLogsLogGroupArn. + # │ - minimum field size of 20, CreateDomainInput.LogPublishingOptions[SEARCH_SLOW_LOGS].CloudWatchLogsLogGroupArn. + # │ - minimum field size of 20, CreateDomainInput.LogPublishingOptions[INDEX_SLOW_LOGS].CloudWatchLogsLogGroupArn. + + dynamic "log_publishing_options" { + for_each = var.log_publishing_index_enabled ? [true] : [] + content { + log_type = "INDEX_SLOW_LOGS" + cloudwatch_log_group_arn = var.log_publishing_index_cloudwatch_log_group_arn + } + } + + dynamic "log_publishing_options" { + for_each = var.log_publishing_search_enabled ? [true] : [] + content { + log_type = "SEARCH_SLOW_LOGS" + cloudwatch_log_group_arn = var.log_publishing_search_cloudwatch_log_group_arn + } + } + + dynamic "log_publishing_options" { + for_each = var.log_publishing_audit_enabled ? [true] : [] + content { + log_type = "AUDIT_LOGS" + cloudwatch_log_group_arn = var.log_publishing_audit_cloudwatch_log_group_arn + } + } + + dynamic "log_publishing_options" { + for_each = var.log_publishing_application_enabled ? [true] : [] + content { + log_type = "ES_APPLICATION_LOGS" + cloudwatch_log_group_arn = var.log_publishing_application_cloudwatch_log_group_arn + } + } + + tags = module.this.tags + + depends_on = [aws_iam_service_linked_role.default] +} diff --git a/outputs.tf b/outputs.tf index 5a379d7..2eb107b 100644 --- a/outputs.tf +++ b/outputs.tf @@ -4,27 +4,27 @@ output "security_group_id" { } output "domain_arn" { - value = join("", aws_elasticsearch_domain.default[*].arn) + value = local.aws_service_domain_arn description = "ARN of the Elasticsearch domain" } output "domain_id" { - value = join("", aws_elasticsearch_domain.default[*].domain_id) + value = local.aws_service_domain_id description = "Unique identifier for the Elasticsearch domain" } output "domain_name" { - value = join("", aws_elasticsearch_domain.default[*].domain_name) + value = local.aws_service_domain_name description = "Name of the Elasticsearch domain" } output "domain_endpoint" { - value = join("", aws_elasticsearch_domain.default[*].endpoint) + value = local.aws_service_domain_endpoint description = "Domain-specific endpoint used to submit index, search, and data upload requests" } output "kibana_endpoint" { - value = join("", aws_elasticsearch_domain.default[*].kibana_endpoint) + value = local.aws_service_domain_kibana_endpoint description = "Domain-specific endpoint for Kibana without https scheme" } diff --git a/variables.tf b/variables.tf index 54b00d5..805bf70 100644 --- a/variables.tf +++ b/variables.tf @@ -115,7 +115,13 @@ variable "iam_authorizing_role_arns" { variable "iam_actions" { type = list(string) default = [] - description = "List of actions to allow for the IAM roles, _e.g._ `es:ESHttpGet`, `es:ESHttpPut`, `es:ESHttpPost`" + description = "List of actions to allow for the user IAM roles, _e.g._ `es:ESHttpGet`, `es:ESHttpPut`, `es:ESHttpPost`" +} + +variable "anonymous_iam_actions" { + type = list(string) + default = [] + description = "List of actions to allow for the anonymous (`*`) IAM roles, _e.g._ `es:ESHttpGet`, `es:ESHttpPut`, `es:ESHttpPost`" } variable "zone_awareness_enabled" { @@ -382,6 +388,18 @@ variable "custom_endpoint_certificate_arn" { default = "" } +variable "aws_service_type" { + type = string + description = "The type of AWS service to deploy (`elasticsearch` or `opensearch`)." + # For backwards comptibility we default to elasticsearch + default = "elasticsearch" + + validation { + condition = contains(["elasticsearch", "opensearch"], var.aws_service_type) + error_message = "Value can only be one of `elasticsearch` or `opensearch`." + } +} + variable "cold_storage_enabled" { type = bool description = "Enables cold storage support." diff --git a/versions.tf b/versions.tf index f365c51..184c5eb 100644 --- a/versions.tf +++ b/versions.tf @@ -6,9 +6,5 @@ terraform { source = "hashicorp/aws" version = ">= 3.35.0" } - null = { - source = "hashicorp/null" - version = ">= 2.0" - } } }