diff --git a/README.md b/README.md index 6d8a608..9112914 100644 --- a/README.md +++ b/README.md @@ -19,25 +19,51 @@ See the `example/vpc` folder for a working module example. ## opensearch ################################################################################ module "opensearch" { - source = "sourcefuse/arc-opensearch/aws" - version = "0.1.2" - namespace = var.namespace - environment = var.environment - name = "${var.project_name}-${var.environment}-opensearch" - engine_version = var.engine_version - instance_type = var.instance_type - instance_count = var.instance_count - enable_vpc_options = true - vpc_id = data.aws_vpc.default.id - subnet_ids = local.private_subnet_ids - ingress_rules = local.ingress_rules - egress_rules = local.egress_rules + source = "sourcefuse/arc-opensearch/aws" + version = "0.1.2" + namespace = var.namespace + environment = var.environment + create_opensearch_serverless = false + create_opensearch = true + name = "${var.project_name}-${var.environment}-opensearch" + engine_version = var.engine_version + instance_type = var.instance_type + instance_count = var.instance_count + enable_vpc_options = true + vpc_id = data.aws_vpc.default.id + subnet_ids = local.private_subnet_ids + ingress_rules = local.ingress_rules + egress_rules = local.egress_rules tags = module.tags.tags } +################################################################################ +## opensearch serverless +################################################################################ + module "opensearch_serverless" { + source = "sourcefuse/arc-opensearch/aws" + version = "0.1.2" + namespace = var.namespace + environment = var.environment + create_opensearch_serverless = true + create_opensearch = false + collection_name = var.collection_name + ingress_rules = local.ingress_rules + egress_rules = local.egress_rules + vpc_name = data.aws_vpc.default.tags["Name"] + vpc_subnet_ids = local.private_subnet_ids + vpc_id = data.aws_vpc.default.id + create_private_access = true + vpc_create_security_group = true + data_lifecycle_policy_rules = local.data_lifecycle_policy_rules + access_policy_rules = local.access_policy_rules + tags = module.tags.tags + +} + ``` -See the `example/non-vpc` folder if you want your os to be public +See the `example/public` folder if you want your os to be public ```hcl ################################################################################ @@ -48,6 +74,8 @@ module "opensearch" { version = "1.0.3" namespace = var.namespace environment = var.environment + create_opensearch = true + create_opensearch_serverless = false name = "${var.project_name}-${var.environment}-opensearch" engine_version = var.engine_version instance_type = var.instance_type @@ -59,6 +87,24 @@ module "opensearch" { tags = module.tags.tags } +################################################################################ +## opensearch serverless +################################################################################ +module "opensearch_serverless" { + source = "sourcefuse/arc-opensearch/aws" + version = "1.0.3" + namespace = var.namespace + environment = var.environment + create_opensearch_serverless = true + create_opensearch = false + collection_name = var.collection_name + create_public_access = true + data_lifecycle_policy_rules = local.data_lifecycle_policy_rules + access_policy_rules = local.access_policy_rules + + tags = module.tags.tags +} + ``` @@ -67,43 +113,33 @@ module "opensearch" { | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.5.0 | -| [aws](#requirement\_aws) | >= 5.0 | -| [random](#requirement\_random) | >= 3.0 | +| [aws](#requirement\_aws) | ~> 5.0 | ## Providers | Name | Version | |------|---------| | [aws](#provider\_aws) | 5.74.0 | -| [random](#provider\_random) | 3.6.3 | ## Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [opensearch](#module\_opensearch) | ./modules/opensearch-domain | n/a | +| [opensearch\_serverless](#module\_opensearch\_serverless) | ./modules/opensearch-serverless | n/a | ## Resources | Name | Type | |------|------| -| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | -| [aws_cloudwatch_log_resource_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_resource_policy) | resource | -| [aws_iam_role.opensearch_cognito_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | -| [aws_iam_role_policy_attachment.opensearch_cognito_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | -| [aws_kms_alias.log_group_key_alias](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | -| [aws_kms_key.log_group_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | -| [aws_opensearch_domain.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain) | resource | -| [aws_opensearch_domain_saml_options.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain_saml_options) | resource | -| [aws_security_group.opensearch_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [aws_ssm_parameter.master_user_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | -| [random_password.master_user_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [access\_policies](#input\_access\_policies) | Custom access policy for OpenSearch domain. If empty, default policy will be used | `string` | `""` | no | +| [access\_policy\_rules](#input\_access\_policy\_rules) | List of rules for the access policy. |
list(object({| `[]` | no | | [advanced\_security\_enabled](#input\_advanced\_security\_enabled) | Enable advanced security options (fine-grained access control) | `bool` | `false` | no | | [anonymous\_auth\_enabled](#input\_anonymous\_auth\_enabled) | Enable anonymous authentication | `bool` | `false` | no | | [auto\_software\_update\_enabled](#input\_auto\_software\_update\_enabled) | Enable automatic software updates for OpenSearch | `bool` | `false` | no | @@ -115,11 +151,16 @@ No modules. | [availability\_zone\_count](#input\_availability\_zone\_count) | The number of availability zones to use for zone awareness. | `number` | `2` | no | | [cognito\_identity\_pool\_id](#input\_cognito\_identity\_pool\_id) | Cognito Identity Pool ID | `string` | `""` | no | | [cognito\_user\_pool\_id](#input\_cognito\_user\_pool\_id) | Cognito User Pool ID | `string` | `""` | no | +| [create\_access\_policy](#input\_create\_access\_policy) | Flag to determine if access policy should be created. | `bool` | `true` | no | +| [create\_data\_lifecycle\_policy](#input\_create\_data\_lifecycle\_policy) | Flag to determine if data lifecycle policy should be created. | `bool` | `true` | no | +| [create\_encryption\_policy](#input\_create\_encryption\_policy) | Flag to determine if encryption policy should be created. | `bool` | `true` | no | | [custom\_certificate\_arn](#input\_custom\_certificate\_arn) | ARN of the ACM certificate for the custom endpoint | `string` | `""` | no | | [custom\_hostname](#input\_custom\_hostname) | Custom domain name for the OpenSearch endpoint | `string` | `""` | no | +| [data\_lifecycle\_policy\_rules](#input\_data\_lifecycle\_policy\_rules) | Data lifecycle policy rules for the indices. |
resource_type = string
resource = list(string)
permissions = list(string)
}))
list(object({|
indexes = list(string)
retention = string
}))
[| no | | [dedicated\_master\_count](#input\_dedicated\_master\_count) | Number of dedicated master instances | `number` | `3` | no | | [dedicated\_master\_enabled](#input\_dedicated\_master\_enabled) | Whether dedicated master is enabled | `bool` | `false` | no | | [dedicated\_master\_type](#input\_dedicated\_master\_type) | Instance type for the dedicated master node | `string` | `"m5.large.search"` | no | +| [description](#input\_description) | A description for the OpenSearch collection. | `string` | `"OpenSearch collection domain for logs and search"` | no | | [ebs\_enabled](#input\_ebs\_enabled) | Whether EBS is enabled for the domain | `bool` | `true` | no | | [egress\_rules](#input\_egress\_rules) | A list of egress rules for the security group. |
{
"indexes": [
"*"
],
"retention": "Unlimited"
}
]
list(object({| `[]` | no | | [enable\_auto\_tune](#input\_enable\_auto\_tune) | Enable Auto-Tune for the domain | `bool` | `false` | no | @@ -128,6 +169,8 @@ No modules. | [enable\_domain\_endpoint\_options](#input\_enable\_domain\_endpoint\_options) | Enable custom domain endpoint options for the OpenSearch domain. | `bool` | `false` | no | | [enable\_encrypt\_at\_rest](#input\_enable\_encrypt\_at\_rest) | Enable encryption at rest for the OpenSearch domain. | `bool` | `false` | no | | [enable\_off\_peak\_window\_options](#input\_enable\_off\_peak\_window\_options) | Enable off-peak window options for the domain | `bool` | `false` | no | +| [enable\_opensearch\_serverless](#input\_enable\_opensearch\_serverless) | Enable OpenSearch Serverless. If true, creates the serverless module; if false, creates the standard module. | `bool` | `false` | no | +| [enable\_public\_access](#input\_enable\_public\_access) | Enable public access for the OpenSearch collection. If false, private access will be used. | `bool` | `false` | no | | [enable\_snapshot\_options](#input\_enable\_snapshot\_options) | Enable snapshot options for the domain | `bool` | `false` | no | | [enable\_vpc\_options](#input\_enable\_vpc\_options) | Enable VPC options for the OpenSearch domain. | `bool` | `false` | no | | [enable\_zone\_awareness](#input\_enable\_zone\_awareness) | Enable zone awareness for the OpenSearch domain. | `bool` | `false` | no | @@ -158,7 +201,9 @@ No modules. | [tags](#input\_tags) | Tags to apply to resources | `map(string)` | n/a | yes | | [throughput](#input\_throughput) | Provisioned throughput for the volume | `number` | `null` | no | | [tls\_security\_policy](#input\_tls\_security\_policy) | TLS security policy for HTTPS endpoints | `string` | `"Policy-Min-TLS-1-2-PFS-2023-10"` | no | +| [type](#input\_type) | The type of OpenSearch collection. | `string` | `"TIMESERIES"` | no | | [use\_iam\_arn\_as\_master\_user](#input\_use\_iam\_arn\_as\_master\_user) | Set to true to use IAM ARN as the master user, false to create a master user. | `bool` | `false` | no | +| [use\_standby\_replicas](#input\_use\_standby\_replicas) | Flag to enable or disable standby replicas. | `bool` | `true` | no | | [use\_ultrawarm](#input\_use\_ultrawarm) | Whether to enable UltraWarm nodes | `bool` | `false` | no | | [volume\_size](#input\_volume\_size) | EBS volume size in GB | `number` | `20` | no | | [volume\_type](#input\_volume\_type) | EBS volume type | `string` | `"gp2"` | no | @@ -171,11 +216,11 @@ No modules. | Name | Description | |------|-------------| -| [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | The ARN of the CloudWatch Log Group associated with OpenSearch. | -| [kms\_key\_id](#output\_kms\_key\_id) | The ID of the KMS Key used for CloudWatch Logs encryption. | | [opensearch\_domain\_arn](#output\_opensearch\_domain\_arn) | The ARN of the OpenSearch domain. | -| [opensearch\_domain\_endpoint](#output\_opensearch\_domain\_endpoint) | The endpoint URL for the OpenSearch domain. | -| [opensearch\_domain\_id](#output\_opensearch\_domain\_id) | The ID of the OpenSearch domain | +| [opensearch\_domain\_endpoint](#output\_opensearch\_domain\_endpoint) | The endpoint of the OpenSearch domain. | +| [opensearch\_domain\_id](#output\_opensearch\_domain\_id) | The unique identifier for the OpenSearch domain. | +| [opensearch\_serverless\_collection\_arn](#output\_opensearch\_serverless\_collection\_arn) | The ARN of the OpenSearch Serverless collection | +| [opensearch\_serverless\_collection\_id](#output\_opensearch\_serverless\_collection\_id) | The ID of the OpenSearch Serverless collection | ## Versioning @@ -218,111 +263,3 @@ the pipeline will kick off and tag the latest git commit. This project is authored by: * SourceFuse ARC Team - - -## Requirements - -No requirements. - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | 5.74.0 | -| [random](#provider\_random) | 3.6.3 | - -## Modules - -No modules. - -## Resources - -| Name | Type | -|------|------| -| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | -| [aws_cloudwatch_log_resource_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_resource_policy) | resource | -| [aws_opensearch_domain.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain) | resource | -| [aws_opensearch_domain_saml_options.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain_saml_options) | resource | -| [aws_security_group.opensearch_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | -| [aws_ssm_parameter.master_user_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | -| [random_password.master_user_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | -| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [access\_policies](#input\_access\_policies) | Access policy for the OpenSearch domain | `string` | n/a | yes | -| [advanced\_security\_enabled](#input\_advanced\_security\_enabled) | Enable advanced security options (fine-grained access control) | `bool` | `false` | no | -| [anonymous\_auth\_enabled](#input\_anonymous\_auth\_enabled) | Enable anonymous authentication | `bool` | `false` | no | -| [auto\_software\_update\_enabled](#input\_auto\_software\_update\_enabled) | Enable automatic software updates for OpenSearch | `bool` | `false` | no | -| [auto\_tune\_cron\_expression](#input\_auto\_tune\_cron\_expression) | Cron expression for Auto-Tune maintenance schedule | `string` | `"0 1 * * ?"` | no | -| [auto\_tune\_desired\_state](#input\_auto\_tune\_desired\_state) | Desired state of Auto-Tune | `string` | `"ENABLED"` | no | -| [auto\_tune\_duration\_unit](#input\_auto\_tune\_duration\_unit) | Duration unit for Auto-Tune maintenance | `string` | `"HOURS"` | no | -| [auto\_tune\_duration\_value](#input\_auto\_tune\_duration\_value) | Duration value for Auto-Tune maintenance | `number` | `1` | no | -| [auto\_tune\_start\_at](#input\_auto\_tune\_start\_at) | Start time for Auto-Tune maintenance | `string` | `"2024-10-23T01:00:00Z"` | no | -| [availability\_zone\_count](#input\_availability\_zone\_count) | The number of availability zones to use for zone awareness. | `number` | `2` | no | -| [cognito\_identity\_pool\_id](#input\_cognito\_identity\_pool\_id) | Cognito Identity Pool ID | `string` | `""` | no | -| [cognito\_role\_arn](#input\_cognito\_role\_arn) | Cognito Role ARN | `string` | `""` | no | -| [cognito\_user\_pool\_id](#input\_cognito\_user\_pool\_id) | Cognito User Pool ID | `string` | `""` | no | -| [cold\_storage\_enabled](#input\_cold\_storage\_enabled) | Flag to enable or disable cold storage options | `bool` | `false` | no | -| [cold\_storage\_retention\_period](#input\_cold\_storage\_retention\_period) | Retention period for cold storage in days | `number` | `30` | no | -| [custom\_certificate\_arn](#input\_custom\_certificate\_arn) | ARN of the ACM certificate for the custom endpoint | `string` | `""` | no | -| [custom\_hostname](#input\_custom\_hostname) | Custom domain name for the OpenSearch endpoint | `string` | `""` | no | -| [dedicated\_master\_count](#input\_dedicated\_master\_count) | Number of dedicated master instances | `number` | `3` | no | -| [dedicated\_master\_enabled](#input\_dedicated\_master\_enabled) | Whether dedicated master is enabled | `bool` | `false` | no | -| [dedicated\_master\_type](#input\_dedicated\_master\_type) | Instance type for the dedicated master node | `string` | `"m4.large.search"` | no | -| [domain\_name](#input\_domain\_name) | Name of the OpenSearch domain | `string` | n/a | yes | -| [ebs\_enabled](#input\_ebs\_enabled) | Whether EBS is enabled for the domain | `bool` | `true` | no | -| [egress\_rules](#input\_egress\_rules) | A list of egress rules for the security group. |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
list(object({| `[]` | no | -| [enable\_auto\_tune](#input\_enable\_auto\_tune) | Enable Auto-Tune for the domain | `bool` | `false` | no | -| [enable\_cognito\_options](#input\_enable\_cognito\_options) | Enable Cognito authentication for the OpenSearch domain | `bool` | `false` | no | -| [enable\_custom\_endpoint](#input\_enable\_custom\_endpoint) | Enable custom domain endpoint | `bool` | `false` | no | -| [enable\_domain\_endpoint\_options](#input\_enable\_domain\_endpoint\_options) | Enable custom domain endpoint options for the OpenSearch domain. | `bool` | `false` | no | -| [enable\_encrypt\_at\_rest](#input\_enable\_encrypt\_at\_rest) | Enable encryption at rest for the OpenSearch domain. | `bool` | `false` | no | -| [enable\_off\_peak\_window\_options](#input\_enable\_off\_peak\_window\_options) | Enable off-peak window options for the domain | `bool` | `false` | no | -| [enable\_snapshot\_options](#input\_enable\_snapshot\_options) | Enable snapshot options for the domain | `bool` | `false` | no | -| [enable\_vpc\_options](#input\_enable\_vpc\_options) | Enable VPC options for the OpenSearch domain. | `bool` | `false` | no | -| [enable\_zone\_awareness](#input\_enable\_zone\_awareness) | Enable zone awareness for the OpenSearch domain. | `bool` | `false` | no | -| [encrypt\_at\_rest\_enabled](#input\_encrypt\_at\_rest\_enabled) | Enable encryption at rest | `bool` | `true` | no | -| [enforce\_https](#input\_enforce\_https) | Force HTTPS on the OpenSearch endpoint | `bool` | `true` | no | -| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_1.0"` | no | -| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | -| [ingress\_rules](#input\_ingress\_rules) | A list of ingress rules for the security group. |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
list(object({| `[]` | no | -| [instance\_count](#input\_instance\_count) | Number of instances in the cluster | `number` | `2` | no | -| [instance\_type](#input\_instance\_type) | Instance type for the OpenSearch domain | `string` | `"m4.large.search"` | no | -| [internal\_user\_database\_enabled](#input\_internal\_user\_database\_enabled) | Enable internal user database for fine-grained access control | `bool` | `true` | no | -| [iops](#input\_iops) | Provisioned IOPS for the volume | `number` | `null` | no | -| [kms\_key\_id](#input\_kms\_key\_id) | KMS key ID for encryption at rest | `string` | `""` | no | -| [log\_group\_name](#input\_log\_group\_name) | The name of the CloudWatch Log Group | `string` | `"arc-example-log-group"` | no | -| [log\_publishing\_enabled](#input\_log\_publishing\_enabled) | Whether to enable the log publishing option. | `bool` | `true` | no | -| [log\_types](#input\_log\_types) | List of log types to publish to CloudWatch (Valid values: INDEX\_SLOW\_LOGS, SEARCH\_SLOW\_LOGS, ES\_APPLICATION\_LOGS, AUDIT\_LOGS) | `list(string)` |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
[| no | -| [master\_user\_arn](#input\_master\_user\_arn) | The ARN of the IAM role for fine-grained access control. Required if use\_iam\_arn\_as\_master\_user is true. | `string` | `""` | no | -| [master\_user\_name](#input\_master\_user\_name) | Master user name for OpenSearch | `string` | `"admin"` | no | -| [node\_to\_node\_encryption\_enabled](#input\_node\_to\_node\_encryption\_enabled) | Enable node-to-node encryption | `bool` | `true` | no | -| [off\_peak\_hours](#input\_off\_peak\_hours) | Off-peak window start time (hours) | `number` | `0` | no | -| [off\_peak\_minutes](#input\_off\_peak\_minutes) | Off-peak window start time (minutes) | `number` | `0` | no | -| [project\_name](#input\_project\_name) | Project name | `string` | `"sourcefuse"` | no | -| [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | -| [rest\_action\_multi\_allow\_explicit\_index](#input\_rest\_action\_multi\_allow\_explicit\_index) | Setting to control whether to allow explicit index usage in multi-document actions | `string` | `"false"` | no | -| [retention\_in\_days](#input\_retention\_in\_days) | The number of days to retain log events in the log group | `number` | `7` | no | -| [saml\_options](#input\_saml\_options) | Configuration block for SAML options in the OpenSearch domain. |
"INDEX_SLOW_LOGS",
"SEARCH_SLOW_LOGS"
]
object({|
enabled = bool
idp_entity_id = optional(string)
idp_metadata_content = optional(string)
roles_key = optional(string)
session_timeout_minutes = optional(number)
subject_key = optional(string)
})
{| no | -| [security\_group\_name](#input\_security\_group\_name) | Name for the security group | `string` | `""` | no | -| [snapshot\_start\_hour](#input\_snapshot\_start\_hour) | Start hour for the automated snapshot | `number` | `0` | no | -| [subnet\_ids](#input\_subnet\_ids) | List of subnet IDs for the OpenSearch domain | `list(string)` | `[]` | no | -| [tags](#input\_tags) | Tags to apply to resources | `map(string)` | n/a | yes | -| [throughput](#input\_throughput) | Provisioned throughput for the volume | `number` | `null` | no | -| [tls\_security\_policy](#input\_tls\_security\_policy) | TLS security policy for HTTPS endpoints | `string` | `"Policy-Min-TLS-1-2-2019-07"` | no | -| [use\_iam\_arn\_as\_master\_user](#input\_use\_iam\_arn\_as\_master\_user) | Set to true to use IAM ARN as the master user, false to create a master user. | `bool` | `false` | no | -| [use\_ultrawarm](#input\_use\_ultrawarm) | Whether to enable UltraWarm nodes | `bool` | `false` | no | -| [volume\_size](#input\_volume\_size) | EBS volume size in GB | `number` | `20` | no | -| [volume\_type](#input\_volume\_type) | EBS volume type | `string` | `"gp2"` | no | -| [vpc\_id](#input\_vpc\_id) | ID of the VPC for OpenSearch domain | `string` | `null` | no | -| [warm\_count](#input\_warm\_count) | Number of UltraWarm instances | `number` | `2` | no | -| [warm\_type](#input\_warm\_type) | UltraWarm node instance type | `string` | `"ultrawarm1.medium.search"` | no | -| [zone\_awareness\_enabled](#input\_zone\_awareness\_enabled) | Whether zone awareness is enabled | `bool` | `true` | no | - -## Outputs - -No outputs. - diff --git a/example/public/README.md b/example/public/README.md index 7e51818..53f0cf7 100644 --- a/example/public/README.md +++ b/example/public/README.md @@ -22,6 +22,7 @@ Terraform module example for supporting AWS OpenSearch. | Name | Source | Version | |------|--------|---------| | [opensearch](#module\_opensearch) | ../.. | n/a | +| [opensearch\_serverless](#module\_opensearch\_serverless) | ../.. | n/a | | [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.6 | ## Resources @@ -34,12 +35,12 @@ Terraform module example for supporting AWS OpenSearch. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_1.0"` | no | +| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_2.15"` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | | [instance\_count](#input\_instance\_count) | Number of instances in the cluster | `number` | `2` | no | | [instance\_type](#input\_instance\_type) | Instance type for the OpenSearch domain | `string` | `"m5.large.search"` | no | +| [name](#input\_name) | Name of the OpenSearch domain | `string` | n/a | yes | | [namespace](#input\_namespace) | Namespace of the project, i.e. refarch | `string` | `"arc"` | no | -| [project\_name](#input\_project\_name) | Project name | `string` | `"sourcefuse"` | no | | [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | ## Outputs @@ -49,44 +50,6 @@ Terraform module example for supporting AWS OpenSearch. | [opensearch\_domain\_arn](#output\_opensearch\_domain\_arn) | The ARN of the OpenSearch domain. | | [opensearch\_domain\_endpoint](#output\_opensearch\_domain\_endpoint) | The endpoint of the OpenSearch domain. | | [opensearch\_domain\_id](#output\_opensearch\_domain\_id) | The unique identifier for the OpenSearch domain. | +| [opensearch\_serverless\_arn](#output\_opensearch\_serverless\_arn) | The ARN of the OpenSearch Serverless collection. | +| [opensearch\_serverless\_collection\_id](#output\_opensearch\_serverless\_collection\_id) | The ID of the OpenSearch Serverless collection | - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | ~> 1.7 | -| [aws](#requirement\_aws) | >= 5.64 | - -## Providers - -No providers. - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [opensearch](#module\_opensearch) | ../.. | n/a | -| [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.6 | - -## Resources - -No resources. - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [access\_policy](#input\_access\_policy) | Access policy for the OpenSearch domain | `string` | n/a | yes | -| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_1.0"` | no | -| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | -| [instance\_count](#input\_instance\_count) | Number of instances in the cluster | `number` | `2` | no | -| [instance\_type](#input\_instance\_type) | Instance type for the OpenSearch domain | `string` | `"m4.large.search"` | no | -| [project\_name](#input\_project\_name) | Project name | `string` | `"sourcefuse"` | no | -| [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | - -## Outputs - -No outputs. - diff --git a/example/public/dev.tfvars b/example/public/dev.tfvars index 0f474b4..b88331c 100644 --- a/example/public/dev.tfvars +++ b/example/public/dev.tfvars @@ -3,3 +3,4 @@ project_name = "arc" engine_version = "OpenSearch_2.15" instance_type = "m5.large.search" instance_count = 2 +name = "arc-opensearch" diff --git a/example/public/locals.tf b/example/public/locals.tf new file mode 100644 index 0000000..2f5b8e0 --- /dev/null +++ b/example/public/locals.tf @@ -0,0 +1,22 @@ +locals { + + ## OpenSearch Serverless Domain + access_policy_rules = [ + { + resource_type = "collection" + resource = ["collection/${var.name}"] + permissions = ["aoss:CreateCollectionItems", "aoss:DeleteCollectionItems", "aoss:UpdateCollectionItems", "aoss:DescribeCollectionItems"] + }, + ] + + data_lifecycle_policy_rules = [ + { + indexes = ["index1", "index2"] + retention = "30d" + }, + { + indexes = ["index3"] + retention = "24h" + } + ] +} diff --git a/example/public/main.tf b/example/public/main.tf index 306428b..04ad776 100644 --- a/example/public/main.tf +++ b/example/public/main.tf @@ -12,6 +12,7 @@ terraform { } } +data "aws_caller_identity" "current" {} provider "aws" { region = var.region @@ -29,18 +30,16 @@ module "tags" { } } -data "aws_caller_identity" "current" {} - ################################################################################ -## opensearch +############################### opensearch ################################# ################################################################################ module "opensearch" { source = "../.." - namespace = var.namespace - environment = var.environment - name = "${var.project_name}-${var.environment}-opensearch" + namespace = var.namespace + environment = var.environment + name = var.name engine_version = var.engine_version instance_type = var.instance_type instance_count = var.instance_count @@ -49,3 +48,22 @@ module "opensearch" { advanced_security_enabled = true tags = module.tags.tags } + + +################################################## +######## OpenSearch Serverless Domain ########### +################################################## + +module "opensearch_serverless" { + source = "../.." + + enable_opensearch_serverless = true + namespace = var.namespace + environment = var.environment + name = var.name + enable_public_access = true + data_lifecycle_policy_rules = local.data_lifecycle_policy_rules + access_policy_rules = local.access_policy_rules + + tags = module.tags.tags +} diff --git a/example/public/output.tf b/example/public/output.tf deleted file mode 100644 index 0a03399..0000000 --- a/example/public/output.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "opensearch_domain_endpoint" { - description = "The endpoint of the OpenSearch domain." - value = module.opensearch.opensearch_domain_endpoint -} - -output "opensearch_domain_arn" { - description = "The ARN of the OpenSearch domain." - value = module.opensearch.opensearch_domain_arn -} - -output "opensearch_domain_id" { - description = "The unique identifier for the OpenSearch domain." - value = trimprefix(module.opensearch.opensearch_domain_id, "${data.aws_caller_identity.current.account_id}/") -} diff --git a/example/public/outputs.tf b/example/public/outputs.tf new file mode 100644 index 0000000..6c19e0b --- /dev/null +++ b/example/public/outputs.tf @@ -0,0 +1,28 @@ +output "opensearch_domain_endpoint" { + description = "The endpoint of the OpenSearch domain." + value = module.opensearch.opensearch_domain_endpoint +} + +output "opensearch_domain_arn" { + description = "The ARN of the OpenSearch domain." + value = module.opensearch.opensearch_domain_arn +} + +output "opensearch_domain_id" { + description = "The unique identifier for the OpenSearch domain." + value = trimprefix(module.opensearch.opensearch_domain_id, "${data.aws_caller_identity.current.account_id}/") +} + +############################################### +## Outputs for OpenSearch Serverless Module +############################################### + +output "opensearch_serverless_arn" { + description = "The ARN of the OpenSearch Serverless collection." + value = module.opensearch_serverless.opensearch_serverless_collection_arn +} + +output "opensearch_serverless_collection_id" { + description = "The ID of the OpenSearch Serverless collection" + value = module.opensearch_serverless.opensearch_serverless_collection_id +} diff --git a/example/public/variables.tf b/example/public/variables.tf index e6f82f9..e9ba5ef 100644 --- a/example/public/variables.tf +++ b/example/public/variables.tf @@ -4,12 +4,6 @@ variable "region" { default = "us-east-1" # Change as needed } -variable "project_name" { - type = string - default = "sourcefuse" - description = "Project name" -} - variable "environment" { type = string default = "dev" @@ -22,11 +16,15 @@ variable "namespace" { default = "arc" } +variable "name" { + description = "Name of the OpenSearch domain" + type = string +} variable "engine_version" { description = "OpenSearch or Elasticsearch engine version" type = string - default = "OpenSearch_1.0" + default = "OpenSearch_2.15" } variable "instance_type" { diff --git a/example/vpc/README.md b/example/vpc/README.md index 228b953..360be85 100644 --- a/example/vpc/README.md +++ b/example/vpc/README.md @@ -21,7 +21,7 @@ Terraform module example for supporting AWS OpenSearch. | Name | Source | Version | |------|--------|---------| -| [opensearch](#module\_opensearch) | ../.. | n/a | +| [opensearch\_serverless](#module\_opensearch\_serverless) | ../.. | n/a | | [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.6 | ## Resources @@ -37,12 +37,12 @@ Terraform module example for supporting AWS OpenSearch. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_1.0"` | no | +| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_2.15"` | no | | [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | | [instance\_count](#input\_instance\_count) | Number of instances in the cluster | `number` | `2` | no | | [instance\_type](#input\_instance\_type) | Instance type for the OpenSearch domain | `string` | `"m5.large.search"` | no | +| [name](#input\_name) | Name of the OpenSearch domain | `string` | n/a | yes | | [namespace](#input\_namespace) | Namespace of the project, i.e. arc | `string` | `"arc"` | no | -| [project\_name](#input\_project\_name) | Project name | `string` | `"sourcefuse"` | no | | [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | | [subnet\_names](#input\_subnet\_names) | List of subnet names to lookup | `list(string)` |
"enabled": false,
"idp_entity_id": null,
"idp_metadata_content": null,
"roles_key": null,
"session_timeout_minutes": null,
"subject_key": null
}
[| no | | [vpc\_name](#input\_vpc\_name) | Name of the VPC to add the resources | `string` | `"arc-poc-vpc"` | no | @@ -51,60 +51,6 @@ Terraform module example for supporting AWS OpenSearch. | Name | Description | |------|-------------| -| [opensearch\_domain\_arn](#output\_opensearch\_domain\_arn) | The ARN of the OpenSearch domain. | -| [opensearch\_domain\_endpoint](#output\_opensearch\_domain\_endpoint) | The endpoint of the OpenSearch domain. | -| [opensearch\_domain\_id](#output\_opensearch\_domain\_id) | The unique identifier for the OpenSearch domain. | +| [opensearch\_serverless\_arn](#output\_opensearch\_serverless\_arn) | The ARN of the OpenSearch Serverless collection. | +| [opensearch\_serverless\_collection\_id](#output\_opensearch\_serverless\_collection\_id) | The ID of the OpenSearch Serverless collection | - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | ~> 1.7 | -| [aws](#requirement\_aws) | >= 5.64 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | 5.74.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [opensearch](#module\_opensearch) | ../.. | n/a | -| [tags](#module\_tags) | sourcefuse/arc-tags/aws | 1.2.6 | - -## Resources - -| Name | Type | -|------|------| -| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | -| [aws_subnet.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source | -| [aws_subnets.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnets) | data source | -| [aws_vpc.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [access\_policy](#input\_access\_policy) | Access policy for the OpenSearch domain | `string` | n/a | yes | -| [domain\_name](#input\_domain\_name) | Name of the OpenSearch domain | `string` | n/a | yes | -| [egress\_rules](#input\_egress\_rules) | A list of egress rules for the security group. |
"arc-poc-private-subnet-private-us-east-1a",
"arc-poc-private-subnet-private-us-east-1b"
]
list(object({| n/a | yes | -| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_1.0"` | no | -| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `"dev"` | no | -| [ingress\_rules](#input\_ingress\_rules) | A list of ingress rules for the security group. |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
list(object({| n/a | yes | -| [instance\_count](#input\_instance\_count) | Number of instances in the cluster | `number` | `2` | no | -| [instance\_type](#input\_instance\_type) | Instance type for the OpenSearch domain | `string` | `"m4.large.search"` | no | -| [namespace](#input\_namespace) | Namespace of the project, i.e. arc | `string` | `"arc"` | no | -| [project\_name](#input\_project\_name) | Project name | `string` | `"sourcefuse"` | no | -| [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | -| [subnet\_names](#input\_subnet\_names) | List of subnet names to lookup | `list(string)` |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
[| no | -| [vpc\_name](#input\_vpc\_name) | Name of the VPC to add the resources | `string` | `"arc-poc-vpc"` | no | - -## Outputs - -No outputs. - diff --git a/example/vpc/dev.tfvars b/example/vpc/dev.tfvars index 0f474b4..1889d9b 100644 --- a/example/vpc/dev.tfvars +++ b/example/vpc/dev.tfvars @@ -3,3 +3,6 @@ project_name = "arc" engine_version = "OpenSearch_2.15" instance_type = "m5.large.search" instance_count = 2 +name = "arc-opensearch" +vpc_name = "vpc-test" +subnet_names = ["test_subnet-0", "test_subnet-1"] diff --git a/example/vpc/locals.tf b/example/vpc/locals.tf index 45f5595..b2f55ae 100644 --- a/example/vpc/locals.tf +++ b/example/vpc/locals.tf @@ -24,4 +24,24 @@ locals { cidr_blocks = ["0.0.0.0/0"] } ] + + ## OpenSearch Serverless Domain + access_policy_rules = [ + { + resource_type = "collection" + resource = ["collection/${var.name}"] + permissions = ["aoss:CreateCollectionItems", "aoss:DeleteCollectionItems", "aoss:UpdateCollectionItems", "aoss:DescribeCollectionItems"] + }, + ] + + data_lifecycle_policy_rules = [ + { + indexes = ["index1", "index2"] + retention = "30d" + }, + { + indexes = ["index3"] + retention = "24h" + } + ] } diff --git a/example/vpc/main.tf b/example/vpc/main.tf index 448da4d..a76ea7b 100644 --- a/example/vpc/main.tf +++ b/example/vpc/main.tf @@ -29,23 +29,43 @@ module "tags" { } ################################################################################ -## opensearch +########################## opensearch ############################# ################################################################################ module "opensearch" { source = "../.." - namespace = var.namespace - environment = var.environment - - name = "${var.project_name}-${var.environment}-opensearch" - engine_version = var.engine_version - instance_type = var.instance_type - instance_count = var.instance_count - enable_vpc_options = true - vpc_id = data.aws_vpc.default.id - subnet_ids = local.private_subnet_ids - ingress_rules = local.ingress_rules - egress_rules = local.egress_rules + namespace = var.namespace + environment = var.environment + name = var.name + engine_version = var.engine_version + instance_type = var.instance_type + instance_count = var.instance_count + enable_vpc_options = true + vpc_id = data.aws_vpc.default.id + subnet_ids = local.private_subnet_ids + ingress_rules = local.ingress_rules + egress_rules = local.egress_rules tags = module.tags.tags } + +################################################## +######## OpenSearch Serverless Domain ########### +################################################## + +# module "opensearch_serverless" { +# source = "../.." + +# enable_opensearch_serverless = true +# namespace = var.namespace +# environment = var.environment +# name = var.name +# ingress_rules = local.ingress_rules +# egress_rules = local.egress_rules +# subnet_ids = local.private_subnet_ids +# vpc_id = data.aws_vpc.default.id +# data_lifecycle_policy_rules = local.data_lifecycle_policy_rules +# access_policy_rules = local.access_policy_rules +# tags = module.tags.tags + +# } diff --git a/example/vpc/output.tf b/example/vpc/output.tf deleted file mode 100644 index 0a03399..0000000 --- a/example/vpc/output.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "opensearch_domain_endpoint" { - description = "The endpoint of the OpenSearch domain." - value = module.opensearch.opensearch_domain_endpoint -} - -output "opensearch_domain_arn" { - description = "The ARN of the OpenSearch domain." - value = module.opensearch.opensearch_domain_arn -} - -output "opensearch_domain_id" { - description = "The unique identifier for the OpenSearch domain." - value = trimprefix(module.opensearch.opensearch_domain_id, "${data.aws_caller_identity.current.account_id}/") -} diff --git a/example/vpc/outputs.tf b/example/vpc/outputs.tf new file mode 100644 index 0000000..0a97e9a --- /dev/null +++ b/example/vpc/outputs.tf @@ -0,0 +1,28 @@ +output "opensearch_domain_endpoint" { + description = "The endpoint of the OpenSearch domain." + value = module.opensearch.opensearch_domain_endpoint +} + +output "opensearch_domain_arn" { + description = "The ARN of the OpenSearch domain." + value = module.opensearch.opensearch_domain_arn +} + +output "opensearch_domain_id" { + description = "The unique identifier for the OpenSearch domain." + value = trimprefix(module.opensearch.opensearch_domain_id, "${data.aws_caller_identity.current.account_id}/") +} + +############################################### +## Outputs for OpenSearch Serverless Module +############################################### + +# output "opensearch_serverless_arn" { +# description = "The ARN of the OpenSearch Serverless collection." +# value = module.opensearch_serverless.opensearch_serverless_collection_arn +# } + +# output "opensearch_serverless_collection_id" { +# description = "The ID of the OpenSearch Serverless collection" +# value = module.opensearch_serverless.opensearch_serverless_collection_id +# } diff --git a/example/vpc/variables.tf b/example/vpc/variables.tf index cb3fc62..1a87008 100644 --- a/example/vpc/variables.tf +++ b/example/vpc/variables.tf @@ -1,13 +1,7 @@ variable "region" { description = "AWS region" type = string - default = "us-east-1" # Change as needed -} - -variable "project_name" { - type = string - default = "sourcefuse" - description = "Project name" + default = "us-east-1" } variable "namespace" { @@ -16,6 +10,11 @@ variable "namespace" { default = "arc" } +variable "name" { + description = "Name of the OpenSearch domain" + type = string +} + variable "subnet_names" { type = list(string) description = "List of subnet names to lookup" @@ -37,7 +36,7 @@ variable "environment" { variable "engine_version" { description = "OpenSearch or Elasticsearch engine version" type = string - default = "OpenSearch_1.0" + default = "OpenSearch_2.15" } variable "instance_type" { diff --git a/main.tf b/main.tf index f33635e..81233bb 100644 --- a/main.tf +++ b/main.tf @@ -1,359 +1,133 @@ -################################################################################ -## defaults -################################################################################ +############################################################################### +#################### opensearch domain ################################## +############################################################################### terraform { required_version = ">= 1.5.0" required_providers { aws = { - version = ">= 5.0" source = "hashicorp/aws" - } - random = { - version = ">= 3.0" - source = "hashicorp/random" + version = "~> 5.0" } } - } data "aws_caller_identity" "current" {} -data "aws_region" "current" {} - -######## OpenSearch Security Group Options ####### -resource "aws_security_group" "opensearch_sg" { - count = var.enable_vpc_options ? 1 : 0 - name = var.security_group_name - description = "Security group for the OpenSearch Domain" - vpc_id = var.vpc_id +module "opensearch" { + source = "./modules/opensearch-domain" + + count = var.enable_opensearch_serverless ? 0 : 1 + + namespace = var.namespace + environment = var.environment + + name = var.name + engine_version = var.engine_version + instance_type = var.instance_type + instance_count = var.instance_count + zone_awareness_enabled = var.zone_awareness_enabled + dedicated_master_enabled = var.dedicated_master_enabled + dedicated_master_type = var.dedicated_master_type + dedicated_master_count = var.dedicated_master_count + use_ultrawarm = var.use_ultrawarm + warm_type = var.warm_type + retention_in_days = var.retention_in_days + warm_count = var.warm_count + ebs_enabled = var.ebs_enabled + volume_type = var.volume_type + volume_size = var.volume_size + iops = var.iops + throughput = var.throughput + + vpc_id = var.vpc_id + subnet_ids = var.subnet_ids + encrypt_at_rest_enabled = var.encrypt_at_rest_enabled + kms_key_id = var.kms_key_id + node_to_node_encryption_enabled = var.node_to_node_encryption_enabled + enforce_https = var.enforce_https + tls_security_policy = var.tls_security_policy + + enable_custom_endpoint = var.enable_custom_endpoint + custom_hostname = var.custom_hostname + custom_certificate_arn = var.custom_certificate_arn + + enable_snapshot_options = var.enable_snapshot_options + snapshot_start_hour = var.snapshot_start_hour + log_types = var.log_types + + access_policies = var.access_policies + advanced_security_enabled = var.advanced_security_enabled + anonymous_auth_enabled = var.anonymous_auth_enabled + internal_user_database_enabled = var.internal_user_database_enabled + master_user_name = var.master_user_name + + enable_auto_tune = var.enable_auto_tune + auto_tune_desired_state = var.auto_tune_desired_state + auto_tune_cron_expression = var.auto_tune_cron_expression + auto_tune_duration_value = var.auto_tune_duration_value + auto_tune_duration_unit = var.auto_tune_duration_unit + auto_tune_start_at = var.auto_tune_start_at + + enable_cognito_options = var.enable_cognito_options + cognito_identity_pool_id = var.cognito_identity_pool_id + cognito_user_pool_id = var.cognito_user_pool_id + + enable_off_peak_window_options = var.enable_off_peak_window_options + off_peak_hours = var.off_peak_hours + off_peak_minutes = var.off_peak_minutes + + enable_zone_awareness = var.enable_zone_awareness + availability_zone_count = var.availability_zone_count + + enable_domain_endpoint_options = var.enable_domain_endpoint_options + enable_encrypt_at_rest = var.enable_encrypt_at_rest + log_publishing_enabled = var.log_publishing_enabled + + enable_vpc_options = var.enable_vpc_options + auto_software_update_enabled = var.auto_software_update_enabled + + saml_options = var.saml_options + + use_iam_arn_as_master_user = var.use_iam_arn_as_master_user + master_user_arn = var.master_user_arn + + ingress_rules = var.ingress_rules + egress_rules = var.egress_rules + + security_group_name = var.security_group_name - dynamic "ingress" { - for_each = var.ingress_rules - content { - from_port = ingress.value.from_port - to_port = ingress.value.to_port - protocol = ingress.value.protocol - cidr_blocks = ingress.value.cidr_blocks - } - } - - dynamic "egress" { - for_each = var.egress_rules - content { - from_port = egress.value.from_port - to_port = egress.value.to_port - protocol = egress.value.protocol - cidr_blocks = egress.value.cidr_blocks - } - } tags = var.tags } -######### Create a KMS key for CloudWatch log group ######## -resource "aws_kms_key" "log_group_key" { - description = "KMS key for CloudWatch log group encryption" - deletion_window_in_days = 30 - enable_key_rotation = true - - policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Sid = "EnableRootPermissions", - Effect = "Allow", - Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }, - Action = [ - "kms:Create*", - "kms:Describe*", - "kms:Enable*", - "kms:List*", - "kms:Put*", - "kms:Update*", - "kms:Revoke*", - "kms:Disable*", - "kms:Get*", - "kms:Delete*", - "kms:ScheduleKeyDeletion", - "kms:CancelKeyDeletion" - ], - Resource = "*" - }, - { - Sid = "AllowCloudWatchLogs", - Effect = "Allow", - Principal = { Service = "logs.${data.aws_region.current.name}.amazonaws.com" }, - Action = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:DescribeKey" - ], - Resource = "*" - } - ] - }) -} - -resource "aws_kms_alias" "log_group_key_alias" { - name = "alias/cloudwatch-os-log-group-key" - target_key_id = aws_kms_key.log_group_key.id -} - -######## CloudWatch Log Group Options ####### -resource "aws_cloudwatch_log_group" "this" { - name = "${var.namespace}-${var.environment}-opensearch-log-group" - retention_in_days = var.retention_in_days - kms_key_id = aws_kms_key.log_group_key.arn - - depends_on = [aws_kms_key.log_group_key] -} - -######## CloudWatch Log Resource Policy Options ####### -resource "aws_cloudwatch_log_resource_policy" "this" { - policy_name = "${var.namespace}-${var.environment}-opensearch-log-group-policy" - - policy_document = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Effect = "Allow", - Principal = { - Service = "opensearchservice.amazonaws.com" - }, - Action = [ - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - Resource = "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:${aws_cloudwatch_log_group.this.name}:*" - } - ] - }) -} - -######### Generate a random password ######### -resource "random_password" "master_user_password" { - count = var.advanced_security_enabled && !var.use_iam_arn_as_master_user ? 1 : 0 - length = 32 - special = true - upper = true - lower = true - numeric = true - override_special = "!@#$%^&*()-_=+[]{}" -} - -######### Store the generated password in ssm ######### -resource "aws_ssm_parameter" "master_user_password" { - count = var.advanced_security_enabled && !var.use_iam_arn_as_master_user ? 1 : 0 - name = "/opensearch/${var.namespace}/${var.environment}/master_user_password" - type = "SecureString" - value = random_password.master_user_password[0].result -} - -######### IAM role for OpenSearch Service Cognito Access ######## -resource "aws_iam_role" "opensearch_cognito_role" { - count = var.enable_cognito_options ? 1 : 0 - name = "${var.namespace}-${var.environment}-opensearch-cognito-role" - - assume_role_policy = jsonencode({ - Version = "2012-10-17", - Statement = [ - { - Effect = "Allow", - Principal = { - Service = "es.amazonaws.com" - }, - Action = "sts:AssumeRole" - } - ] - }) -} - -####### Attach the Aws managed policy to the role ####### -resource "aws_iam_role_policy_attachment" "opensearch_cognito_policy_attachment" { - count = var.enable_cognito_options ? 1 : 0 - role = aws_iam_role.opensearch_cognito_role[0].name - policy_arn = "arn:aws:iam::aws:policy/AmazonOpenSearchServiceCognitoAccess" -} - -############################################## -######## OpenSearch Domain Options ########### -############################################## -resource "aws_opensearch_domain" "this" { - domain_name = var.name - engine_version = var.engine_version - - ######## Cluster configuration ####### - cluster_config { - instance_type = var.instance_type - instance_count = var.instance_count - zone_awareness_enabled = var.zone_awareness_enabled - dedicated_master_enabled = var.dedicated_master_enabled - dedicated_master_type = var.dedicated_master_enabled ? var.dedicated_master_type : null - dedicated_master_count = var.dedicated_master_enabled ? var.dedicated_master_count : 0 - warm_enabled = var.use_ultrawarm ? true : false - warm_type = var.use_ultrawarm ? var.warm_type : null - warm_count = var.use_ultrawarm ? var.warm_count : null - - dynamic "zone_awareness_config" { - for_each = var.enable_zone_awareness ? [1] : [] - content { - availability_zone_count = var.availability_zone_count - } - } - } - - ######## EBS options ####### - ebs_options { - ebs_enabled = var.ebs_enabled - volume_type = var.volume_type - volume_size = var.volume_size - iops = var.iops - throughput = var.throughput - } - - ######## VPC Options ####### - dynamic "vpc_options" { - for_each = var.enable_vpc_options ? [1] : [] - content { - subnet_ids = var.subnet_ids - security_group_ids = [aws_security_group.opensearch_sg[0].id] - } - } - - ######## Snapshot options ####### - dynamic "snapshot_options" { - for_each = var.enable_snapshot_options ? [1] : [] - content { - automated_snapshot_start_hour = var.snapshot_start_hour - } - } - - ######## Encryption options ####### - dynamic "encrypt_at_rest" { - for_each = var.enable_encrypt_at_rest ? [1] : [] - content { - enabled = var.encrypt_at_rest_enabled - kms_key_id = var.kms_key_id != "" ? var.kms_key_id : null - } - } - - ######## Node-to-node encryption options ####### - node_to_node_encryption { - enabled = var.node_to_node_encryption_enabled - } - - ######## Domain endpoint ####### - dynamic "domain_endpoint_options" { - for_each = var.enable_domain_endpoint_options ? [1] : [] - content { - enforce_https = var.enforce_https - tls_security_policy = var.tls_security_policy - custom_endpoint = var.enable_custom_endpoint ? var.custom_hostname : null - custom_endpoint_certificate_arn = var.enable_custom_endpoint ? var.custom_certificate_arn : null - } - } - - ###### access_policies ####### - access_policies = var.access_policies != "" ? var.access_policies : local.access_policy - - ######## Log publishing options ####### - dynamic "log_publishing_options" { - for_each = var.log_types - content { - log_type = log_publishing_options.value - enabled = var.log_publishing_enabled - cloudwatch_log_group_arn = aws_cloudwatch_log_group.this.arn - } - } - - ######## Advanced Security Options ####### - dynamic "advanced_security_options" { - for_each = var.advanced_security_enabled ? [1] : [] - content { - enabled = true - anonymous_auth_enabled = var.anonymous_auth_enabled - internal_user_database_enabled = var.internal_user_database_enabled +################################################## +######## OpenSearch Serverless Domain ########### +################################################## - ######### master user options or IAM ARN ######## - dynamic "master_user_options" { - for_each = var.use_iam_arn_as_master_user ? [] : [1] - content { - master_user_name = var.master_user_name - master_user_password = aws_ssm_parameter.master_user_password[0].value - master_user_arn = var.use_iam_arn_as_master_user ? var.master_user_arn : null - } - } - } - } - ######## Auto-Tune options ####### - dynamic "auto_tune_options" { - for_each = var.enable_auto_tune ? [1] : [] - content { - desired_state = var.auto_tune_desired_state +module "opensearch_serverless" { + source = "./modules/opensearch-serverless" - dynamic "maintenance_schedule" { - for_each = var.enable_auto_tune ? [1] : [] - content { - cron_expression_for_recurrence = var.auto_tune_cron_expression - duration { - value = var.auto_tune_duration_value - unit = var.auto_tune_duration_unit - } - start_at = var.auto_tune_start_at - } - } - } - } + count = var.enable_opensearch_serverless ? 1 : 0 - ######## Cognito options ####### - dynamic "cognito_options" { - for_each = var.enable_cognito_options ? [1] : [] - content { - enabled = true - identity_pool_id = var.cognito_identity_pool_id - role_arn = aws_iam_role.opensearch_cognito_role[0].arn - user_pool_id = var.cognito_user_pool_id - } - } + namespace = var.namespace + environment = var.environment - ######## Off-peak window options ####### - dynamic "off_peak_window_options" { - for_each = var.enable_off_peak_window_options ? [1] : [] - content { - enabled = true - off_peak_window { - window_start_time { - hours = var.off_peak_hours - minutes = var.off_peak_minutes - } - } - } - } - - ######## Software update options ####### - software_update_options { - auto_software_update_enabled = var.auto_software_update_enabled - } + name = var.name + description = var.description + use_standby_replicas = var.use_standby_replicas + type = var.type + create_encryption_policy = var.create_encryption_policy + subnet_ids = var.subnet_ids + vpc_id = var.vpc_id + create_access_policy = var.create_access_policy + access_policy_rules = var.access_policy_rules + create_data_lifecycle_policy = var.create_data_lifecycle_policy + data_lifecycle_policy_rules = var.data_lifecycle_policy_rules + ingress_rules = var.ingress_rules + egress_rules = var.egress_rules + security_group_name = var.security_group_name + enable_public_access = var.enable_public_access - ######## Tags ####### tags = var.tags } - -######## SAML Options ####### -resource "aws_opensearch_domain_saml_options" "this" { - count = var.saml_options.enabled ? 1 : 0 - domain_name = aws_opensearch_domain.this.domain_name - - saml_options { - idp { - entity_id = var.saml_options.idp_entity_id - metadata_content = var.saml_options.idp_metadata_content - } - roles_key = var.saml_options.roles_key - session_timeout_minutes = var.saml_options.session_timeout_minutes - subject_key = var.saml_options.subject_key - } -} diff --git a/modules/opensearch-domain/.terraform.lock.hcl b/modules/opensearch-domain/.terraform.lock.hcl new file mode 100644 index 0000000..17ebbef --- /dev/null +++ b/modules/opensearch-domain/.terraform.lock.hcl @@ -0,0 +1,45 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.75.1" + constraints = ">= 5.0.0" + hashes = [ + "h1:PIBnv1Mi0tX2GF6qUSdps3IouABeTqVgJZ4aAzIVzdI=", + "zh:1075825e7311a8d2d233fd453a173910e891b0320e8a7698af44d1f90b02621d", + "zh:203c5d09a03fcaa946defb8459f01227f2fcda07df768f74777beb328d6751ae", + "zh:21bc79ccb09bfdeb711a3a5226c6c4a457ac7c4bb781dbda6ade7be38461739f", + "zh:2bac969855b62a0ff6716954be29387a1f9793626059122cda4681206396e309", + "zh:4b65ea5b51058f05b9ec8797f76184e19e5b38a609029fe2226af3fa4ad289b3", + "zh:5065d7df357fb3ee2b0a2520bbcff6335c0c47bfb9e8e9932bad088c3ab7efd3", + "zh:678a4015a4cd26af5c2b30dfd9290b8a01e900668fa0fec6585dfd1838f1cebd", + "zh:6ddc5dfdd4a0dddca027db99a7bfa9a0978933119d63af81acb6020728405119", + "zh:98c0d48b09842c444dbcbddd279e5b5b1e44113951817a8ecc28896bb4ad1dd7", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:aad169fea072842c0b54f1ff95f1ec6558d6c5af3ea4c159308583db59003b09", + "zh:bd2625ed8e1ff29ac6ed3a810d7b68a090add5fcb2fce4122669bd37e1eb9f1d", + "zh:c6f57625e26a6ef1ffb49bfa0e6148496ad12d80c857f6bb222e21f293a2a78a", + "zh:c7cd085326c5eb88804b11a4bc0fbc8376f06138f4b9624fb25cd06ea8687cdd", + "zh:f60c98139f983817d4d08f4138b1e53f31f91176ff638631e8dd38b6de36fce0", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.6.3" + constraints = ">= 3.0.0" + hashes = [ + "h1:Fnaec9vA8sZ8BXVlN3Xn9Jz3zghSETIKg7ch8oXhxno=", + "zh:04ceb65210251339f07cd4611885d242cd4d0c7306e86dda9785396807c00451", + "zh:448f56199f3e99ff75d5c0afacae867ee795e4dfda6cb5f8e3b2a72ec3583dd8", + "zh:4b4c11ccfba7319e901df2dac836b1ae8f12185e37249e8d870ee10bb87a13fe", + "zh:4fa45c44c0de582c2edb8a2e054f55124520c16a39b2dfc0355929063b6395b1", + "zh:588508280501a06259e023b0695f6a18149a3816d259655c424d068982cbdd36", + "zh:737c4d99a87d2a4d1ac0a54a73d2cb62974ccb2edbd234f333abd079a32ebc9e", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:a357ab512e5ebc6d1fda1382503109766e21bbfdfaa9ccda43d313c122069b30", + "zh:c51bfb15e7d52cc1a2eaec2a903ac2aff15d162c172b1b4c17675190e8147615", + "zh:e0951ee6fa9df90433728b96381fb867e3db98f66f735e0c3e24f8f16903f0ad", + "zh:e3cdcb4e73740621dabd82ee6a37d6cfce7fee2a03d8074df65086760f5cf556", + "zh:eff58323099f1bd9a0bec7cb04f717e7f1b2774c7d612bf7581797e1622613a0", + ] +} diff --git a/modules/opensearch-domain/README.md b/modules/opensearch-domain/README.md new file mode 100644 index 0000000..572309a --- /dev/null +++ b/modules/opensearch-domain/README.md @@ -0,0 +1,116 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5.0 | +| [aws](#requirement\_aws) | >= 5.0 | +| [random](#requirement\_random) | >= 3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.75.1 | +| [random](#provider\_random) | 3.6.3 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | +| [aws_cloudwatch_log_resource_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_resource_policy) | resource | +| [aws_iam_role.opensearch_cognito_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.opensearch_cognito_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_kms_alias.log_group_key_alias](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource | +| [aws_kms_key.log_group_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource | +| [aws_opensearch_domain.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain) | resource | +| [aws_opensearch_domain_saml_options.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearch_domain_saml_options) | resource | +| [aws_security_group.opensearch_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_ssm_parameter.master_user_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | +| [random_password.master_user_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [access\_policies](#input\_access\_policies) | Custom access policy for OpenSearch domain. If empty, default policy will be used | `string` | `""` | no | +| [advanced\_security\_enabled](#input\_advanced\_security\_enabled) | Enable advanced security options (fine-grained access control) | `bool` | `false` | no | +| [anonymous\_auth\_enabled](#input\_anonymous\_auth\_enabled) | Enable anonymous authentication | `bool` | `false` | no | +| [auto\_software\_update\_enabled](#input\_auto\_software\_update\_enabled) | Enable automatic software updates for OpenSearch | `bool` | `false` | no | +| [auto\_tune\_cron\_expression](#input\_auto\_tune\_cron\_expression) | Cron expression for Auto-Tune maintenance schedule | `string` | `"0 1 * * ?"` | no | +| [auto\_tune\_desired\_state](#input\_auto\_tune\_desired\_state) | Desired state of Auto-Tune | `string` | `"ENABLED"` | no | +| [auto\_tune\_duration\_unit](#input\_auto\_tune\_duration\_unit) | Duration unit for Auto-Tune maintenance | `string` | `"HOURS"` | no | +| [auto\_tune\_duration\_value](#input\_auto\_tune\_duration\_value) | Duration value for Auto-Tune maintenance | `number` | `1` | no | +| [auto\_tune\_start\_at](#input\_auto\_tune\_start\_at) | Start time for Auto-Tune maintenance | `string` | `"2024-10-23T01:00:00Z"` | no | +| [availability\_zone\_count](#input\_availability\_zone\_count) | The number of availability zones to use for zone awareness. | `number` | `2` | no | +| [cognito\_identity\_pool\_id](#input\_cognito\_identity\_pool\_id) | Cognito Identity Pool ID | `string` | `""` | no | +| [cognito\_user\_pool\_id](#input\_cognito\_user\_pool\_id) | Cognito User Pool ID | `string` | `""` | no | +| [custom\_certificate\_arn](#input\_custom\_certificate\_arn) | ARN of the ACM certificate for the custom endpoint | `string` | `""` | no | +| [custom\_hostname](#input\_custom\_hostname) | Custom domain name for the OpenSearch endpoint | `string` | `""` | no | +| [dedicated\_master\_count](#input\_dedicated\_master\_count) | Number of dedicated master instances | `number` | `3` | no | +| [dedicated\_master\_enabled](#input\_dedicated\_master\_enabled) | Whether dedicated master is enabled | `bool` | `false` | no | +| [dedicated\_master\_type](#input\_dedicated\_master\_type) | Instance type for the dedicated master node | `string` | `"m5.large.search"` | no | +| [ebs\_enabled](#input\_ebs\_enabled) | Whether EBS is enabled for the domain | `bool` | `true` | no | +| [egress\_rules](#input\_egress\_rules) | A list of egress rules for the security group. |
"arc-poc-private-subnet-private-us-east-1a",
"arc-poc-private-subnet-private-us-east-1b"
]
list(object({| `[]` | no | +| [enable\_auto\_tune](#input\_enable\_auto\_tune) | Enable Auto-Tune for the domain | `bool` | `false` | no | +| [enable\_cognito\_options](#input\_enable\_cognito\_options) | Enable Cognito authentication for the OpenSearch domain | `bool` | `false` | no | +| [enable\_custom\_endpoint](#input\_enable\_custom\_endpoint) | Enable custom domain endpoint | `bool` | `false` | no | +| [enable\_domain\_endpoint\_options](#input\_enable\_domain\_endpoint\_options) | Enable custom domain endpoint options for the OpenSearch domain. | `bool` | `false` | no | +| [enable\_encrypt\_at\_rest](#input\_enable\_encrypt\_at\_rest) | Enable encryption at rest for the OpenSearch domain. | `bool` | `false` | no | +| [enable\_off\_peak\_window\_options](#input\_enable\_off\_peak\_window\_options) | Enable off-peak window options for the domain | `bool` | `false` | no | +| [enable\_snapshot\_options](#input\_enable\_snapshot\_options) | Enable snapshot options for the domain | `bool` | `false` | no | +| [enable\_vpc\_options](#input\_enable\_vpc\_options) | Enable VPC options for the OpenSearch domain. | `bool` | `false` | no | +| [enable\_zone\_awareness](#input\_enable\_zone\_awareness) | Enable zone awareness for the OpenSearch domain. | `bool` | `false` | no | +| [encrypt\_at\_rest\_enabled](#input\_encrypt\_at\_rest\_enabled) | Enable encryption at rest | `bool` | `true` | no | +| [enforce\_https](#input\_enforce\_https) | Force HTTPS on the OpenSearch endpoint | `bool` | `true` | no | +| [engine\_version](#input\_engine\_version) | OpenSearch or Elasticsearch engine version | `string` | `"OpenSearch_1.0"` | no | +| [environment](#input\_environment) | Name of the environment, i.e. dev, stage, prod | `string` | n/a | yes | +| [ingress\_rules](#input\_ingress\_rules) | A list of ingress rules for the security group. |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
list(object({| `[]` | no | +| [instance\_count](#input\_instance\_count) | Number of instances in the cluster | `number` | `2` | no | +| [instance\_type](#input\_instance\_type) | Instance type for the OpenSearch domain | `string` | `"m5.large.search"` | no | +| [internal\_user\_database\_enabled](#input\_internal\_user\_database\_enabled) | Enable internal user database for fine-grained access control | `bool` | `true` | no | +| [iops](#input\_iops) | Provisioned IOPS for the volume | `number` | `null` | no | +| [kms\_key\_id](#input\_kms\_key\_id) | KMS key ID for encryption at rest | `string` | `""` | no | +| [log\_publishing\_enabled](#input\_log\_publishing\_enabled) | Whether to enable the log publishing option. | `bool` | `true` | no | +| [log\_types](#input\_log\_types) | List of log types to publish to CloudWatch (Valid values: INDEX\_SLOW\_LOGS, SEARCH\_SLOW\_LOGS, ES\_APPLICATION\_LOGS, AUDIT\_LOGS) | `list(string)` |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
[| no | +| [master\_user\_arn](#input\_master\_user\_arn) | The ARN of the IAM role for fine-grained access control. Required if use\_iam\_arn\_as\_master\_user is true. | `string` | `""` | no | +| [master\_user\_name](#input\_master\_user\_name) | Master user name for OpenSearch | `string` | `"admin"` | no | +| [name](#input\_name) | Name of the OpenSearch domain | `string` | n/a | yes | +| [namespace](#input\_namespace) | Namespace of the project, i.e. arc | `string` | n/a | yes | +| [node\_to\_node\_encryption\_enabled](#input\_node\_to\_node\_encryption\_enabled) | Enable node-to-node encryption | `bool` | `true` | no | +| [off\_peak\_hours](#input\_off\_peak\_hours) | Off-peak window start time (hours) | `number` | `0` | no | +| [off\_peak\_minutes](#input\_off\_peak\_minutes) | Off-peak window start time (minutes) | `number` | `0` | no | +| [retention\_in\_days](#input\_retention\_in\_days) | The number of days to retain log events in the log group | `number` | `7` | no | +| [saml\_options](#input\_saml\_options) | Configuration block for SAML options in the OpenSearch domain. |
"INDEX_SLOW_LOGS",
"SEARCH_SLOW_LOGS"
]
object({|
enabled = bool
idp_entity_id = optional(string)
idp_metadata_content = optional(string)
roles_key = optional(string)
session_timeout_minutes = optional(number)
subject_key = optional(string)
})
{| no | +| [security\_group\_name](#input\_security\_group\_name) | Name for the security group | `string` | `""` | no | +| [snapshot\_start\_hour](#input\_snapshot\_start\_hour) | Start hour for the automated snapshot | `number` | `0` | no | +| [subnet\_ids](#input\_subnet\_ids) | List of subnet IDs for the OpenSearch domain | `list(string)` | `[]` | no | +| [tags](#input\_tags) | Tags to apply to resources | `map(string)` | n/a | yes | +| [throughput](#input\_throughput) | Provisioned throughput for the volume | `number` | `null` | no | +| [tls\_security\_policy](#input\_tls\_security\_policy) | TLS security policy for HTTPS endpoints | `string` | `"Policy-Min-TLS-1-2-PFS-2023-10"` | no | +| [use\_iam\_arn\_as\_master\_user](#input\_use\_iam\_arn\_as\_master\_user) | Set to true to use IAM ARN as the master user, false to create a master user. | `bool` | `false` | no | +| [use\_ultrawarm](#input\_use\_ultrawarm) | Whether to enable UltraWarm nodes | `bool` | `false` | no | +| [volume\_size](#input\_volume\_size) | EBS volume size in GB | `number` | `20` | no | +| [volume\_type](#input\_volume\_type) | EBS volume type | `string` | `"gp2"` | no | +| [vpc\_id](#input\_vpc\_id) | ID of the VPC for OpenSearch domain | `string` | `null` | no | +| [warm\_count](#input\_warm\_count) | Number of UltraWarm instances | `number` | `2` | no | +| [warm\_type](#input\_warm\_type) | UltraWarm node instance type | `string` | `"ultrawarm1.medium.search"` | no | +| [zone\_awareness\_enabled](#input\_zone\_awareness\_enabled) | Whether zone awareness is enabled | `bool` | `true` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | The ARN of the CloudWatch Log Group associated with OpenSearch. | +| [kms\_key\_id](#output\_kms\_key\_id) | The ID of the KMS Key used for CloudWatch Logs encryption. | +| [opensearch\_domain\_arn](#output\_opensearch\_domain\_arn) | The ARN of the OpenSearch domain. | +| [opensearch\_domain\_endpoint](#output\_opensearch\_domain\_endpoint) | The endpoint URL for the OpenSearch domain. | +| [opensearch\_domain\_id](#output\_opensearch\_domain\_id) | The ID of the OpenSearch domain | + diff --git a/locals.tf b/modules/opensearch-domain/locals.tf similarity index 100% rename from locals.tf rename to modules/opensearch-domain/locals.tf diff --git a/modules/opensearch-domain/main.tf b/modules/opensearch-domain/main.tf new file mode 100644 index 0000000..f33635e --- /dev/null +++ b/modules/opensearch-domain/main.tf @@ -0,0 +1,359 @@ +################################################################################ +## defaults +################################################################################ +terraform { + required_version = ">= 1.5.0" + + required_providers { + aws = { + version = ">= 5.0" + source = "hashicorp/aws" + } + random = { + version = ">= 3.0" + source = "hashicorp/random" + } + } + +} + +data "aws_caller_identity" "current" {} + +data "aws_region" "current" {} + +######## OpenSearch Security Group Options ####### +resource "aws_security_group" "opensearch_sg" { + count = var.enable_vpc_options ? 1 : 0 + name = var.security_group_name + description = "Security group for the OpenSearch Domain" + vpc_id = var.vpc_id + + dynamic "ingress" { + for_each = var.ingress_rules + content { + from_port = ingress.value.from_port + to_port = ingress.value.to_port + protocol = ingress.value.protocol + cidr_blocks = ingress.value.cidr_blocks + } + } + + dynamic "egress" { + for_each = var.egress_rules + content { + from_port = egress.value.from_port + to_port = egress.value.to_port + protocol = egress.value.protocol + cidr_blocks = egress.value.cidr_blocks + } + } + tags = var.tags +} + +######### Create a KMS key for CloudWatch log group ######## +resource "aws_kms_key" "log_group_key" { + description = "KMS key for CloudWatch log group encryption" + deletion_window_in_days = 30 + enable_key_rotation = true + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Sid = "EnableRootPermissions", + Effect = "Allow", + Principal = { AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" }, + Action = [ + "kms:Create*", + "kms:Describe*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion" + ], + Resource = "*" + }, + { + Sid = "AllowCloudWatchLogs", + Effect = "Allow", + Principal = { Service = "logs.${data.aws_region.current.name}.amazonaws.com" }, + Action = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ], + Resource = "*" + } + ] + }) +} + +resource "aws_kms_alias" "log_group_key_alias" { + name = "alias/cloudwatch-os-log-group-key" + target_key_id = aws_kms_key.log_group_key.id +} + +######## CloudWatch Log Group Options ####### +resource "aws_cloudwatch_log_group" "this" { + name = "${var.namespace}-${var.environment}-opensearch-log-group" + retention_in_days = var.retention_in_days + kms_key_id = aws_kms_key.log_group_key.arn + + depends_on = [aws_kms_key.log_group_key] +} + +######## CloudWatch Log Resource Policy Options ####### +resource "aws_cloudwatch_log_resource_policy" "this" { + policy_name = "${var.namespace}-${var.environment}-opensearch-log-group-policy" + + policy_document = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = { + Service = "opensearchservice.amazonaws.com" + }, + Action = [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + Resource = "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:${aws_cloudwatch_log_group.this.name}:*" + } + ] + }) +} + +######### Generate a random password ######### +resource "random_password" "master_user_password" { + count = var.advanced_security_enabled && !var.use_iam_arn_as_master_user ? 1 : 0 + length = 32 + special = true + upper = true + lower = true + numeric = true + override_special = "!@#$%^&*()-_=+[]{}" +} + +######### Store the generated password in ssm ######### +resource "aws_ssm_parameter" "master_user_password" { + count = var.advanced_security_enabled && !var.use_iam_arn_as_master_user ? 1 : 0 + name = "/opensearch/${var.namespace}/${var.environment}/master_user_password" + type = "SecureString" + value = random_password.master_user_password[0].result +} + +######### IAM role for OpenSearch Service Cognito Access ######## +resource "aws_iam_role" "opensearch_cognito_role" { + count = var.enable_cognito_options ? 1 : 0 + name = "${var.namespace}-${var.environment}-opensearch-cognito-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = { + Service = "es.amazonaws.com" + }, + Action = "sts:AssumeRole" + } + ] + }) +} + +####### Attach the Aws managed policy to the role ####### +resource "aws_iam_role_policy_attachment" "opensearch_cognito_policy_attachment" { + count = var.enable_cognito_options ? 1 : 0 + role = aws_iam_role.opensearch_cognito_role[0].name + policy_arn = "arn:aws:iam::aws:policy/AmazonOpenSearchServiceCognitoAccess" +} + +############################################## +######## OpenSearch Domain Options ########### +############################################## +resource "aws_opensearch_domain" "this" { + domain_name = var.name + engine_version = var.engine_version + + ######## Cluster configuration ####### + cluster_config { + instance_type = var.instance_type + instance_count = var.instance_count + zone_awareness_enabled = var.zone_awareness_enabled + dedicated_master_enabled = var.dedicated_master_enabled + dedicated_master_type = var.dedicated_master_enabled ? var.dedicated_master_type : null + dedicated_master_count = var.dedicated_master_enabled ? var.dedicated_master_count : 0 + warm_enabled = var.use_ultrawarm ? true : false + warm_type = var.use_ultrawarm ? var.warm_type : null + warm_count = var.use_ultrawarm ? var.warm_count : null + + dynamic "zone_awareness_config" { + for_each = var.enable_zone_awareness ? [1] : [] + content { + availability_zone_count = var.availability_zone_count + } + } + } + + ######## EBS options ####### + ebs_options { + ebs_enabled = var.ebs_enabled + volume_type = var.volume_type + volume_size = var.volume_size + iops = var.iops + throughput = var.throughput + } + + ######## VPC Options ####### + dynamic "vpc_options" { + for_each = var.enable_vpc_options ? [1] : [] + content { + subnet_ids = var.subnet_ids + security_group_ids = [aws_security_group.opensearch_sg[0].id] + } + } + + ######## Snapshot options ####### + dynamic "snapshot_options" { + for_each = var.enable_snapshot_options ? [1] : [] + content { + automated_snapshot_start_hour = var.snapshot_start_hour + } + } + + ######## Encryption options ####### + dynamic "encrypt_at_rest" { + for_each = var.enable_encrypt_at_rest ? [1] : [] + content { + enabled = var.encrypt_at_rest_enabled + kms_key_id = var.kms_key_id != "" ? var.kms_key_id : null + } + } + + ######## Node-to-node encryption options ####### + node_to_node_encryption { + enabled = var.node_to_node_encryption_enabled + } + + ######## Domain endpoint ####### + dynamic "domain_endpoint_options" { + for_each = var.enable_domain_endpoint_options ? [1] : [] + content { + enforce_https = var.enforce_https + tls_security_policy = var.tls_security_policy + custom_endpoint = var.enable_custom_endpoint ? var.custom_hostname : null + custom_endpoint_certificate_arn = var.enable_custom_endpoint ? var.custom_certificate_arn : null + } + } + + ###### access_policies ####### + access_policies = var.access_policies != "" ? var.access_policies : local.access_policy + + ######## Log publishing options ####### + dynamic "log_publishing_options" { + for_each = var.log_types + content { + log_type = log_publishing_options.value + enabled = var.log_publishing_enabled + cloudwatch_log_group_arn = aws_cloudwatch_log_group.this.arn + } + } + + ######## Advanced Security Options ####### + dynamic "advanced_security_options" { + for_each = var.advanced_security_enabled ? [1] : [] + content { + enabled = true + anonymous_auth_enabled = var.anonymous_auth_enabled + internal_user_database_enabled = var.internal_user_database_enabled + + ######### master user options or IAM ARN ######## + dynamic "master_user_options" { + for_each = var.use_iam_arn_as_master_user ? [] : [1] + content { + master_user_name = var.master_user_name + master_user_password = aws_ssm_parameter.master_user_password[0].value + master_user_arn = var.use_iam_arn_as_master_user ? var.master_user_arn : null + } + } + + } + } + ######## Auto-Tune options ####### + dynamic "auto_tune_options" { + for_each = var.enable_auto_tune ? [1] : [] + content { + desired_state = var.auto_tune_desired_state + + dynamic "maintenance_schedule" { + for_each = var.enable_auto_tune ? [1] : [] + content { + cron_expression_for_recurrence = var.auto_tune_cron_expression + duration { + value = var.auto_tune_duration_value + unit = var.auto_tune_duration_unit + } + start_at = var.auto_tune_start_at + } + } + } + } + + ######## Cognito options ####### + dynamic "cognito_options" { + for_each = var.enable_cognito_options ? [1] : [] + content { + enabled = true + identity_pool_id = var.cognito_identity_pool_id + role_arn = aws_iam_role.opensearch_cognito_role[0].arn + user_pool_id = var.cognito_user_pool_id + } + } + + ######## Off-peak window options ####### + dynamic "off_peak_window_options" { + for_each = var.enable_off_peak_window_options ? [1] : [] + content { + enabled = true + off_peak_window { + window_start_time { + hours = var.off_peak_hours + minutes = var.off_peak_minutes + } + } + } + } + + ######## Software update options ####### + software_update_options { + auto_software_update_enabled = var.auto_software_update_enabled + } + + ######## Tags ####### + tags = var.tags +} + +######## SAML Options ####### +resource "aws_opensearch_domain_saml_options" "this" { + count = var.saml_options.enabled ? 1 : 0 + domain_name = aws_opensearch_domain.this.domain_name + + saml_options { + idp { + entity_id = var.saml_options.idp_entity_id + metadata_content = var.saml_options.idp_metadata_content + } + roles_key = var.saml_options.roles_key + session_timeout_minutes = var.saml_options.session_timeout_minutes + subject_key = var.saml_options.subject_key + } +} diff --git a/output.tf b/modules/opensearch-domain/outputs.tf similarity index 100% rename from output.tf rename to modules/opensearch-domain/outputs.tf diff --git a/modules/opensearch-domain/variables.tf b/modules/opensearch-domain/variables.tf new file mode 100644 index 0000000..07d258b --- /dev/null +++ b/modules/opensearch-domain/variables.tf @@ -0,0 +1,398 @@ +variable "name" { + description = "Name of the OpenSearch domain" + type = string +} + +variable "environment" { + type = string + description = "Name of the environment, i.e. dev, stage, prod" +} + +variable "namespace" { + type = string + description = "Namespace of the project, i.e. arc" +} + +variable "engine_version" { + description = "OpenSearch or Elasticsearch engine version" + type = string + default = "OpenSearch_1.0" +} + +variable "instance_type" { + description = "Instance type for the OpenSearch domain" + type = string + default = "m5.large.search" +} + +variable "instance_count" { + description = "Number of instances in the cluster" + type = number + default = 2 +} + +variable "zone_awareness_enabled" { + description = "Whether zone awareness is enabled" + type = bool + default = true +} + +variable "dedicated_master_enabled" { + description = "Whether dedicated master is enabled" + type = bool + default = false +} + +variable "dedicated_master_type" { + description = "Instance type for the dedicated master node" + type = string + default = "m5.large.search" +} + +variable "dedicated_master_count" { + description = "Number of dedicated master instances" + type = number + default = 3 +} + +variable "use_ultrawarm" { + description = "Whether to enable UltraWarm nodes" + type = bool + default = false +} + +variable "warm_type" { + description = "UltraWarm node instance type" + type = string + default = "ultrawarm1.medium.search" +} + +variable "retention_in_days" { + description = "The number of days to retain log events in the log group" + type = number + default = 7 +} + +variable "warm_count" { + description = "Number of UltraWarm instances" + type = number + default = 2 +} + +variable "ebs_enabled" { + description = "Whether EBS is enabled for the domain" + type = bool + default = true +} + +variable "volume_type" { + description = "EBS volume type" + type = string + default = "gp2" +} + +variable "volume_size" { + description = "EBS volume size in GB" + type = number + default = 20 +} + +variable "iops" { + description = "Provisioned IOPS for the volume" + type = number + default = null +} + +variable "throughput" { + description = "Provisioned throughput for the volume" + type = number + default = null +} + +variable "vpc_id" { + description = "ID of the VPC for OpenSearch domain" + type = string + default = null +} + +variable "subnet_ids" { + description = "List of subnet IDs for the OpenSearch domain" + type = list(string) + default = [] +} + +variable "encrypt_at_rest_enabled" { + description = "Enable encryption at rest" + type = bool + default = true +} + +variable "kms_key_id" { + description = "KMS key ID for encryption at rest" + type = string + default = "" +} + +variable "node_to_node_encryption_enabled" { + description = "Enable node-to-node encryption" + type = bool + default = true +} + +variable "enforce_https" { + description = "Force HTTPS on the OpenSearch endpoint" + type = bool + default = true +} + +variable "tls_security_policy" { + description = "TLS security policy for HTTPS endpoints" + type = string + default = "Policy-Min-TLS-1-2-PFS-2023-10" +} + +variable "enable_custom_endpoint" { + description = "Enable custom domain endpoint" + type = bool + default = false +} + +variable "custom_hostname" { + description = "Custom domain name for the OpenSearch endpoint" + type = string + default = "" +} + +variable "custom_certificate_arn" { + description = "ARN of the ACM certificate for the custom endpoint" + type = string + default = "" +} + +variable "enable_snapshot_options" { + description = "Enable snapshot options for the domain" + type = bool + default = false +} + +variable "snapshot_start_hour" { + description = "Start hour for the automated snapshot" + type = number + default = 0 +} + +variable "log_types" { + description = "List of log types to publish to CloudWatch (Valid values: INDEX_SLOW_LOGS, SEARCH_SLOW_LOGS, ES_APPLICATION_LOGS, AUDIT_LOGS)" + type = list(string) + default = ["INDEX_SLOW_LOGS", "SEARCH_SLOW_LOGS"] +} + +variable "access_policies" { + description = "Custom access policy for OpenSearch domain. If empty, default policy will be used" + type = string + default = "" +} + +variable "advanced_security_enabled" { + description = "Enable advanced security options (fine-grained access control)" + type = bool + default = false +} + +variable "anonymous_auth_enabled" { + description = "Enable anonymous authentication" + type = bool + default = false +} + +variable "internal_user_database_enabled" { + description = "Enable internal user database for fine-grained access control" + type = bool + default = true +} + +variable "master_user_name" { + description = "Master user name for OpenSearch" + type = string + default = "admin" +} + +variable "enable_auto_tune" { + description = "Enable Auto-Tune for the domain" + type = bool + default = false +} + +variable "auto_tune_desired_state" { + description = "Desired state of Auto-Tune" + type = string + default = "ENABLED" +} + +variable "auto_tune_cron_expression" { + description = "Cron expression for Auto-Tune maintenance schedule" + type = string + default = "0 1 * * ?" +} + +variable "auto_tune_duration_value" { + description = "Duration value for Auto-Tune maintenance" + type = number + default = 1 +} + +variable "auto_tune_duration_unit" { + description = "Duration unit for Auto-Tune maintenance" + type = string + default = "HOURS" +} + +variable "auto_tune_start_at" { + description = "Start time for Auto-Tune maintenance" + type = string + default = "2024-10-23T01:00:00Z" +} + +variable "enable_cognito_options" { + description = "Enable Cognito authentication for the OpenSearch domain" + type = bool + default = false +} + +variable "cognito_identity_pool_id" { + description = "Cognito Identity Pool ID" + type = string + default = "" +} + +variable "cognito_user_pool_id" { + description = "Cognito User Pool ID" + type = string + default = "" +} + +variable "enable_off_peak_window_options" { + description = "Enable off-peak window options for the domain" + type = bool + default = false +} + +variable "off_peak_hours" { + description = "Off-peak window start time (hours)" + type = number + default = 0 +} + +variable "off_peak_minutes" { + description = "Off-peak window start time (minutes)" + type = number + default = 0 +} + +variable "tags" { + description = "Tags to apply to resources" + type = map(string) +} + +variable "enable_zone_awareness" { + description = "Enable zone awareness for the OpenSearch domain." + type = bool + default = false +} + +variable "availability_zone_count" { + description = "The number of availability zones to use for zone awareness." + type = number + default = 2 +} + +variable "enable_domain_endpoint_options" { + description = "Enable custom domain endpoint options for the OpenSearch domain." + type = bool + default = false +} + +variable "enable_encrypt_at_rest" { + description = "Enable encryption at rest for the OpenSearch domain." + type = bool + default = false +} + +variable "log_publishing_enabled" { + description = "Whether to enable the log publishing option." + type = bool + default = true +} + +variable "enable_vpc_options" { + description = "Enable VPC options for the OpenSearch domain." + type = bool + default = false +} + +variable "auto_software_update_enabled" { + description = "Enable automatic software updates for OpenSearch" + type = bool + default = false +} + +# SAML Options +variable "saml_options" { + description = "Configuration block for SAML options in the OpenSearch domain." + type = object({ + enabled = bool + idp_entity_id = optional(string) + idp_metadata_content = optional(string) + roles_key = optional(string) + session_timeout_minutes = optional(number) + subject_key = optional(string) + }) + default = { + enabled = false + idp_entity_id = null + idp_metadata_content = null + roles_key = null + session_timeout_minutes = null + subject_key = null + } +} + +variable "use_iam_arn_as_master_user" { + description = "Set to true to use IAM ARN as the master user, false to create a master user." + type = bool + default = false +} + +variable "master_user_arn" { + description = "The ARN of the IAM role for fine-grained access control. Required if use_iam_arn_as_master_user is true." + type = string + default = "" +} + +variable "ingress_rules" { + description = "A list of ingress rules for the security group." + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = list(string) + })) + default = [] +} + +variable "egress_rules" { + description = "A list of egress rules for the security group." + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = list(string) + })) + default = [] +} + +variable "security_group_name" { + description = "Name for the security group" + type = string + default = "" +} diff --git a/modules/opensearch-serverless/.terraform.lock.hcl b/modules/opensearch-serverless/.terraform.lock.hcl new file mode 100644 index 0000000..b837f94 --- /dev/null +++ b/modules/opensearch-serverless/.terraform.lock.hcl @@ -0,0 +1,44 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.75.1" + hashes = [ + "h1:PIBnv1Mi0tX2GF6qUSdps3IouABeTqVgJZ4aAzIVzdI=", + "zh:1075825e7311a8d2d233fd453a173910e891b0320e8a7698af44d1f90b02621d", + "zh:203c5d09a03fcaa946defb8459f01227f2fcda07df768f74777beb328d6751ae", + "zh:21bc79ccb09bfdeb711a3a5226c6c4a457ac7c4bb781dbda6ade7be38461739f", + "zh:2bac969855b62a0ff6716954be29387a1f9793626059122cda4681206396e309", + "zh:4b65ea5b51058f05b9ec8797f76184e19e5b38a609029fe2226af3fa4ad289b3", + "zh:5065d7df357fb3ee2b0a2520bbcff6335c0c47bfb9e8e9932bad088c3ab7efd3", + "zh:678a4015a4cd26af5c2b30dfd9290b8a01e900668fa0fec6585dfd1838f1cebd", + "zh:6ddc5dfdd4a0dddca027db99a7bfa9a0978933119d63af81acb6020728405119", + "zh:98c0d48b09842c444dbcbddd279e5b5b1e44113951817a8ecc28896bb4ad1dd7", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:aad169fea072842c0b54f1ff95f1ec6558d6c5af3ea4c159308583db59003b09", + "zh:bd2625ed8e1ff29ac6ed3a810d7b68a090add5fcb2fce4122669bd37e1eb9f1d", + "zh:c6f57625e26a6ef1ffb49bfa0e6148496ad12d80c857f6bb222e21f293a2a78a", + "zh:c7cd085326c5eb88804b11a4bc0fbc8376f06138f4b9624fb25cd06ea8687cdd", + "zh:f60c98139f983817d4d08f4138b1e53f31f91176ff638631e8dd38b6de36fce0", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.6.3" + constraints = ">= 3.0.0" + hashes = [ + "h1:Fnaec9vA8sZ8BXVlN3Xn9Jz3zghSETIKg7ch8oXhxno=", + "zh:04ceb65210251339f07cd4611885d242cd4d0c7306e86dda9785396807c00451", + "zh:448f56199f3e99ff75d5c0afacae867ee795e4dfda6cb5f8e3b2a72ec3583dd8", + "zh:4b4c11ccfba7319e901df2dac836b1ae8f12185e37249e8d870ee10bb87a13fe", + "zh:4fa45c44c0de582c2edb8a2e054f55124520c16a39b2dfc0355929063b6395b1", + "zh:588508280501a06259e023b0695f6a18149a3816d259655c424d068982cbdd36", + "zh:737c4d99a87d2a4d1ac0a54a73d2cb62974ccb2edbd234f333abd079a32ebc9e", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:a357ab512e5ebc6d1fda1382503109766e21bbfdfaa9ccda43d313c122069b30", + "zh:c51bfb15e7d52cc1a2eaec2a903ac2aff15d162c172b1b4c17675190e8147615", + "zh:e0951ee6fa9df90433728b96381fb867e3db98f66f735e0c3e24f8f16903f0ad", + "zh:e3cdcb4e73740621dabd82ee6a37d6cfce7fee2a03d8074df65086760f5cf556", + "zh:eff58323099f1bd9a0bec7cb04f717e7f1b2774c7d612bf7581797e1622613a0", + ] +} diff --git a/modules/opensearch-serverless/README.md b/modules/opensearch-serverless/README.md new file mode 100644 index 0000000..e89c1b7 --- /dev/null +++ b/modules/opensearch-serverless/README.md @@ -0,0 +1,70 @@ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.5.0 | +| [aws](#requirement\_aws) | >= 5.0 | +| [random](#requirement\_random) | >= 3.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | 5.75.1 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.opensearch_custom_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_role.opensearch_access_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_role_policy_attachment.opensearch_access_policy_attachment](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | +| [aws_opensearchserverless_access_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_access_policy) | resource | +| [aws_opensearchserverless_collection.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_collection) | resource | +| [aws_opensearchserverless_lifecycle_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_lifecycle_policy) | resource | +| [aws_opensearchserverless_security_policy.encryption](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_security_policy) | resource | +| [aws_opensearchserverless_security_policy.private_network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_security_policy) | resource | +| [aws_opensearchserverless_security_policy.public_network](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_security_policy) | resource | +| [aws_opensearchserverless_vpc_endpoint.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/opensearchserverless_vpc_endpoint) | resource | +| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [access\_policy\_rules](#input\_access\_policy\_rules) | List of rules for the access policy. |
"enabled": false,
"idp_entity_id": null,
"idp_metadata_content": null,
"roles_key": null,
"session_timeout_minutes": null,
"subject_key": null
}
list(object({| `[]` | no | +| [collection\_name](#input\_collection\_name) | The name of the OpenSearch collection. | `string` | n/a | yes | +| [create\_access\_policy](#input\_create\_access\_policy) | Flag to determine if access policy should be created. | `bool` | `true` | no | +| [create\_data\_lifecycle\_policy](#input\_create\_data\_lifecycle\_policy) | Flag to determine if data lifecycle policy should be created. | `bool` | `true` | no | +| [create\_encryption\_policy](#input\_create\_encryption\_policy) | Flag to determine if encryption policy should be created. | `bool` | `true` | no | +| [create\_network\_policy](#input\_create\_network\_policy) | Flag to determine if network policy should be created. | `bool` | `true` | no | +| [create\_private\_access](#input\_create\_private\_access) | Enable or disable private access for the OpenSearch collection | `bool` | `false` | no | +| [create\_public\_access](#input\_create\_public\_access) | Enable or disable public access for the OpenSearch collection | `bool` | `false` | no | +| [data\_lifecycle\_policy\_rules](#input\_data\_lifecycle\_policy\_rules) | Data lifecycle policy rules for the indices. |
resource_type = string
resource = list(string)
permissions = list(string)
}))
list(object({|
indexes = list(string)
retention = string
}))
[| no | +| [description](#input\_description) | A description for the OpenSearch collection. | `string` | `"OpenSearch collection domain for logs and search"` | no | +| [egress\_rules](#input\_egress\_rules) | A list of egress rules for the security group. |
{
"indexes": [
"*"
],
"retention": "Unlimited"
}
]
list(object({| `[]` | no | +| [environment](#input\_environment) | Name of the environment, i.e. dev, stage, prod | `string` | `"dev"` | no | +| [ingress\_rules](#input\_ingress\_rules) | A list of ingress rules for the security group. |
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
list(object({| `[]` | no | +| [namespace](#input\_namespace) | Namespace of the project, i.e. arc | `string` | `"arc"` | no | +| [tags](#input\_tags) | Tags to assign to the OpenSearch collection. | `map(string)` | `{}` | no | +| [type](#input\_type) | The type of OpenSearch collection. | `string` | n/a | yes | +| [use\_standby\_replicas](#input\_use\_standby\_replicas) | Flag to enable or disable standby replicas. | `bool` | `true` | no | +| [vpc\_create\_security\_group](#input\_vpc\_create\_security\_group) | Flag to determine if a security group for VPC endpoint should be created. | `bool` | `false` | no | +| [vpc\_id](#input\_vpc\_id) | The VPC ID for the VPC endpoint. | `string` | `null` | no | +| [vpc\_name](#input\_vpc\_name) | The name of the VPC endpoint. | `string` | `null` | no | +| [vpc\_security\_group\_name](#input\_vpc\_security\_group\_name) | The name of the VPC endpoint security group. | `string` | `"opensearch-vpc-sg"` | no | +| [vpc\_subnet\_ids](#input\_vpc\_subnet\_ids) | A list of subnet IDs for the VPC endpoint. | `list(string)` | `[]` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [opensearch\_collection\_arn](#output\_opensearch\_collection\_arn) | The ARN of the OpenSearch collection | +| [opensearch\_collection\_id](#output\_opensearch\_collection\_id) | The ID of the OpenSearch Serverless collection | + diff --git a/modules/opensearch-serverless/main.tf b/modules/opensearch-serverless/main.tf new file mode 100644 index 0000000..e64839a --- /dev/null +++ b/modules/opensearch-serverless/main.tf @@ -0,0 +1,219 @@ +################################################################################ +## defaults +################################################################################ +terraform { + required_version = ">= 1.5.0" + + required_providers { + aws = { + version = ">= 5.0" + source = "hashicorp/aws" + } + random = { + version = ">= 3.0" + source = "hashicorp/random" + } + } + +} + +data "aws_caller_identity" "current" {} + +resource "aws_opensearchserverless_collection" "this" { + name = var.name + description = var.description + standby_replicas = var.use_standby_replicas ? "ENABLED" : "DISABLED" + type = var.type + tags = var.tags + depends_on = [aws_opensearchserverless_security_policy.encryption] +} + +######### encryption policy ######### +resource "aws_opensearchserverless_security_policy" "encryption" { + count = var.create_encryption_policy ? 1 : 0 + name = "${var.namespace}-${var.environment}-encryption" + type = "encryption" + description = "Encryption policy for OpenSearch collection" + policy = jsonencode(merge( + { + "Rules" = [ + { + "Resource" = ["collection/${var.name}"] + "ResourceType" = "collection" + } + ], + }, + { + "AWSOwnedKey" = true + } + )) +} + + +########## Public access policy ######### +resource "aws_opensearchserverless_security_policy" "public_network" { + count = var.enable_public_access ? 1 : 0 + name = "${var.namespace}-${var.environment}-public-policy" + type = "network" + description = "Public access policy for ${var.name}" + policy = jsonencode([{ + "Rules" = [ + { + "ResourceType" = "collection", + "Resource" = ["collection/${var.name}"] + }, + { + "ResourceType" = "dashboard", + "Resource" = ["collection/${var.name}"] + }, + ], + "AllowFromPublic" = true, + }]) +} + +########## Private access policy ######### +resource "aws_opensearchserverless_security_policy" "private_network" { + count = var.enable_public_access ? 0 : 1 + name = "${var.namespace}-${var.environment}-private-policy" + type = "network" + description = "Private VPC access policy for ${var.name}" + policy = jsonencode([{ + "Rules" = [ + { + "ResourceType" = "collection", + "Resource" = ["collection/${var.name}"] + }, + { + "ResourceType" = "dashboard", + "Resource" = ["collection/${var.name}"] + } + ], + "AllowFromPublic" = false, + "SourceVPCEs" = [aws_opensearchserverless_vpc_endpoint.this[0].id], + }]) +} + +########## VPC endpoint ######### +resource "aws_opensearchserverless_vpc_endpoint" "this" { + count = var.enable_public_access ? 0 : 1 + name = "${var.namespace}-${var.environment}-vpc-endpoint" + subnet_ids = var.subnet_ids + vpc_id = var.vpc_id + security_group_ids = [aws_security_group.this[0].id] +} + +########## access role ######### +resource "aws_iam_role" "opensearch_access_role" { + count = var.create_access_policy ? 1 : 0 + name = "${var.namespace}-${var.environment}-role" + assume_role_policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Principal" : { + "Service" : "es.amazonaws.com" + }, + "Action" : "sts:AssumeRole" + } + ] + }) +} + +########## role custom policy ######### +resource "aws_iam_policy" "opensearch_custom_policy" { + count = var.create_access_policy ? 1 : 0 + name = "${var.namespace}-${var.environment}-os-custompolicy" + description = "Custom policy for OpenSearch Serverless access" + policy = jsonencode({ + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : [ + "aoss:ReadDocument", + "aoss:WriteDocument", + "aoss:DescribeIndex", + "aoss:*" + ], + "Resource" : "*" + } + ] + }) +} + +########## role attachment policy ######### +resource "aws_iam_role_policy_attachment" "opensearch_access_policy_attachment" { + count = var.create_access_policy ? 1 : 0 + role = aws_iam_role.opensearch_access_role[0].name + policy_arn = aws_iam_policy.opensearch_custom_policy[0].arn +} + +########## access policy ######### +resource "aws_opensearchserverless_access_policy" "this" { + count = var.create_access_policy ? 1 : 0 + name = "${var.namespace}-${var.environment}-access-policy" + type = "data" + description = "Network policy description" + + # Define the policy with required permissions + policy = jsonencode([ + for rule in var.access_policy_rules : { + "Rules" = [ + { + "ResourceType" = rule.resource_type + "Resource" = rule.resource + "Permission" = rule.permissions + } + ], + "Principal" = [ + "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${aws_iam_role.opensearch_access_role[0].name}" + ] + }]) +} + +########## lifecycle policy ######### +resource "aws_opensearchserverless_lifecycle_policy" "this" { + count = var.create_data_lifecycle_policy ? 1 : 0 + name = "${var.namespace}-${var.environment}-data-policy" + type = "retention" + description = "Data lifecycle policy description" + policy = jsonencode({ + Rules = [ + for rule in var.data_lifecycle_policy_rules : { + ResourceType = "index", + Resource = [for index in rule.indexes : "index/${var.name}/${index}"], + MinIndexRetention = rule.retention != "Unlimited" ? rule.retention : null + } + ] + }) +} + +########### Security Group for serverless ######### +resource "aws_security_group" "this" { + count = var.enable_public_access ? 0 : 1 + name = var.security_group_name + description = "Security group for the OpenSearch collection" + vpc_id = var.vpc_id + + dynamic "ingress" { + for_each = var.ingress_rules + content { + from_port = ingress.value.from_port + to_port = ingress.value.to_port + protocol = ingress.value.protocol + cidr_blocks = ingress.value.cidr_blocks + } + } + + dynamic "egress" { + for_each = var.egress_rules + content { + from_port = egress.value.from_port + to_port = egress.value.to_port + protocol = egress.value.protocol + cidr_blocks = egress.value.cidr_blocks + } + } + tags = var.tags +} diff --git a/modules/opensearch-serverless/outputs.tf b/modules/opensearch-serverless/outputs.tf new file mode 100644 index 0000000..ae61933 --- /dev/null +++ b/modules/opensearch-serverless/outputs.tf @@ -0,0 +1,9 @@ +output "opensearch_collection_id" { + description = "The ID of the OpenSearch Serverless collection" + value = aws_opensearchserverless_collection.this.id +} + +output "opensearch_collection_arn" { + description = "The ARN of the OpenSearch collection" + value = aws_opensearchserverless_collection.this.arn +} diff --git a/modules/opensearch-serverless/variables.tf b/modules/opensearch-serverless/variables.tf new file mode 100644 index 0000000..affafbf --- /dev/null +++ b/modules/opensearch-serverless/variables.tf @@ -0,0 +1,127 @@ +variable "name" { + description = "The name of the OpenSearch collection." + type = string +} + +variable "environment" { + type = string + description = "Name of the environment, i.e. dev, stage, prod" + default = "dev" +} + +variable "namespace" { + type = string + description = "Namespace of the project, i.e. arc" + default = "arc" +} + +variable "description" { + description = "A description for the OpenSearch collection." + type = string + default = "OpenSearch collection domain for logs and search" +} + +variable "use_standby_replicas" { + description = "Flag to enable or disable standby replicas." + type = bool + default = true +} + +variable "type" { + description = "The type of OpenSearch collection." + type = string +} + +variable "tags" { + description = "Tags to assign to the OpenSearch collection." + type = map(string) + default = {} +} + +variable "create_encryption_policy" { + description = "Flag to determine if encryption policy should be created." + type = bool + default = true +} + +variable "subnet_ids" { + description = "A list of subnet IDs for the VPC endpoint." + type = list(string) + default = [] +} + +variable "vpc_id" { + description = "The VPC ID for the VPC endpoint." + type = string + default = null +} + +variable "create_access_policy" { + description = "Flag to determine if access policy should be created." + type = bool + default = true +} + +variable "access_policy_rules" { + description = "List of rules for the access policy." + type = list(object({ + resource_type = string + resource = list(string) + permissions = list(string) + })) + default = [] +} + +variable "create_data_lifecycle_policy" { + description = "Flag to determine if data lifecycle policy should be created." + type = bool + default = true +} + +variable "data_lifecycle_policy_rules" { + description = "Data lifecycle policy rules for the indices." + type = list(object({ + indexes = list(string) + retention = string + })) + default = [ + { + indexes = ["*"] + retention = "Unlimited" + } + ] +} + +variable "ingress_rules" { + description = "A list of ingress rules for the security group." + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = list(string) + })) + default = [] +} + +variable "egress_rules" { + description = "A list of egress rules for the security group." + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = list(string) + })) + default = [] +} + +variable "security_group_name" { + description = "The name of the VPC endpoint security group." + type = string + default = "opensearch-vpc-sg" +} + +variable "enable_public_access" { + description = "Enable public access for the OpenSearch collection. If false, private access will be used." + type = bool + default = false +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..8cc48e7 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,29 @@ +output "opensearch_domain_endpoint" { + description = "The endpoint of the OpenSearch domain." + value = length(module.opensearch) > 0 ? module.opensearch[0].opensearch_domain_endpoint : null +} + +output "opensearch_domain_arn" { + description = "The ARN of the OpenSearch domain." + value = length(module.opensearch) > 0 ? module.opensearch[0].opensearch_domain_arn : null +} + +output "opensearch_domain_id" { + description = "The unique identifier for the OpenSearch domain." + value = length(module.opensearch) > 0 ? trimprefix(module.opensearch[0].opensearch_domain_id, "${data.aws_caller_identity.current.account_id}/") : null +} + + +############################################### +## Outputs for OpenSearch Serverless Module +############################################### + +output "opensearch_serverless_collection_arn" { + value = length(module.opensearch_serverless) > 0 ? module.opensearch_serverless[0].opensearch_collection_arn : null + description = "The ARN of the OpenSearch Serverless collection" +} + +output "opensearch_serverless_collection_id" { + value = length(module.opensearch_serverless) > 0 ? module.opensearch_serverless[0].opensearch_collection_id : null + description = "The ID of the OpenSearch Serverless collection" +} diff --git a/variables.tf b/variables.tf index 07d258b..00a3591 100644 --- a/variables.tf +++ b/variables.tf @@ -1,7 +1,6 @@ -variable "name" { - description = "Name of the OpenSearch domain" - type = string -} +################################################# +############ common variables ############ +################################################# variable "environment" { type = string @@ -13,6 +12,43 @@ variable "namespace" { description = "Namespace of the project, i.e. arc" } +variable "ingress_rules" { + description = "A list of ingress rules for the security group." + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = list(string) + })) + default = [] +} + +variable "egress_rules" { + description = "A list of egress rules for the security group." + type = list(object({ + from_port = number + to_port = number + protocol = string + cidr_blocks = list(string) + })) + default = [] +} + +variable "enable_opensearch_serverless" { + description = "Enable OpenSearch Serverless. If true, creates the serverless module; if false, creates the standard module." + type = bool + default = false +} + +##################################################################### +#################### opensearch domain ######################## +##################################################################### + +variable "name" { + description = "Name of the OpenSearch domain" + type = string +} + variable "engine_version" { description = "OpenSearch or Elasticsearch engine version" type = string @@ -369,30 +405,79 @@ variable "master_user_arn" { default = "" } -variable "ingress_rules" { - description = "A list of ingress rules for the security group." +variable "security_group_name" { + description = "Name for the security group" + type = string + default = "" +} + + +################################################## +######## OpenSearch Serverless Domain ########### +################################################## + +variable "description" { + description = "A description for the OpenSearch collection." + type = string + default = "OpenSearch collection domain for logs and search" +} + +variable "use_standby_replicas" { + description = "Flag to enable or disable standby replicas." + type = bool + default = true +} + +variable "type" { + description = "The type of OpenSearch collection." + type = string + default = "TIMESERIES" +} + +variable "create_encryption_policy" { + description = "Flag to determine if encryption policy should be created." + type = bool + default = true +} + +variable "create_access_policy" { + description = "Flag to determine if access policy should be created." + type = bool + default = true +} + +variable "access_policy_rules" { + description = "List of rules for the access policy." type = list(object({ - from_port = number - to_port = number - protocol = string - cidr_blocks = list(string) + resource_type = string + resource = list(string) + permissions = list(string) })) default = [] } -variable "egress_rules" { - description = "A list of egress rules for the security group." +variable "create_data_lifecycle_policy" { + description = "Flag to determine if data lifecycle policy should be created." + type = bool + default = true +} + +variable "data_lifecycle_policy_rules" { + description = "Data lifecycle policy rules for the indices." type = list(object({ - from_port = number - to_port = number - protocol = string - cidr_blocks = list(string) + indexes = list(string) + retention = string })) - default = [] + default = [ + { + indexes = ["*"] + retention = "Unlimited" + } + ] } -variable "security_group_name" { - description = "Name for the security group" - type = string - default = "" +variable "enable_public_access" { + description = "Enable public access for the OpenSearch collection. If false, private access will be used." + type = bool + default = false }
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))