summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/aws/plugins/modules/opensearch.py
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/aws/plugins/modules/opensearch.py')
-rw-r--r--ansible_collections/community/aws/plugins/modules/opensearch.py1501
1 files changed, 1501 insertions, 0 deletions
diff --git a/ansible_collections/community/aws/plugins/modules/opensearch.py b/ansible_collections/community/aws/plugins/modules/opensearch.py
new file mode 100644
index 00000000..7ed8c072
--- /dev/null
+++ b/ansible_collections/community/aws/plugins/modules/opensearch.py
@@ -0,0 +1,1501 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: opensearch
+short_description: Creates OpenSearch or ElasticSearch domain
+description:
+ - Creates or modify a Amazon OpenSearch Service domain.
+version_added: 4.0.0
+author: "Sebastien Rosset (@sebastien-rosset)"
+options:
+ state:
+ description:
+ - Creates or modifies an existing OpenSearch domain.
+ - Deletes an OpenSearch domain.
+ required: false
+ type: str
+ choices: ['present', 'absent']
+ default: present
+ domain_name:
+ description:
+ - The name of the Amazon OpenSearch/ElasticSearch Service domain.
+ - Domain names are unique across the domains owned by an account within an AWS region.
+ required: true
+ type: str
+ engine_version:
+ description:
+ ->
+ The engine version to use. For example, 'ElasticSearch_7.10' or 'OpenSearch_1.1'.
+ ->
+ If the currently running version is not equal to I(engine_version),
+ a cluster upgrade is triggered.
+ ->
+ It may not be possible to upgrade directly from the currently running version
+ to I(engine_version). In that case, the upgrade is performed incrementally by
+ upgrading to the highest compatible version, then repeat the operation until
+ the cluster is running at the target version.
+ ->
+ The upgrade operation fails if there is no path from current version to I(engine_version).
+ ->
+ See OpenSearch documentation for upgrade compatibility.
+ required: false
+ type: str
+ allow_intermediate_upgrades:
+ description:
+ - >
+ If true, allow OpenSearch domain to be upgraded through one or more intermediate versions.
+ - >
+ If false, do not allow OpenSearch domain to be upgraded through intermediate versions.
+ The upgrade operation fails if it's not possible to ugrade to I(engine_version) directly.
+ required: false
+ type: bool
+ default: true
+ cluster_config:
+ description:
+ - Parameters for the cluster configuration of an OpenSearch Service domain.
+ type: dict
+ suboptions:
+ instance_type:
+ description:
+ - Type of the instances to use for the domain.
+ required: false
+ type: str
+ instance_count:
+ description:
+ - Number of instances for the domain.
+ required: false
+ type: int
+ zone_awareness:
+ description:
+ - A boolean value to indicate whether zone awareness is enabled.
+ required: false
+ type: bool
+ availability_zone_count:
+ description:
+ - >
+ An integer value to indicate the number of availability zones for a domain when zone awareness is enabled.
+ This should be equal to number of subnets if VPC endpoints is enabled.
+ required: false
+ type: int
+ dedicated_master:
+ description:
+ - A boolean value to indicate whether a dedicated master node is enabled.
+ required: false
+ type: bool
+ dedicated_master_instance_type:
+ description:
+ - The instance type for a dedicated master node.
+ required: false
+ type: str
+ dedicated_master_instance_count:
+ description:
+ - Total number of dedicated master nodes, active and on standby, for the domain.
+ required: false
+ type: int
+ warm_enabled:
+ description:
+ - True to enable UltraWarm storage.
+ required: false
+ type: bool
+ warm_type:
+ description:
+ - The instance type for the OpenSearch domain's warm nodes.
+ required: false
+ type: str
+ warm_count:
+ description:
+ - The number of UltraWarm nodes in the domain.
+ required: false
+ type: int
+ cold_storage_options:
+ description:
+ - Specifies the ColdStorageOptions config for a Domain.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - True to enable cold storage. Supported on Elasticsearch 7.9 or above.
+ required: false
+ type: bool
+ ebs_options:
+ description:
+ - Parameters to configure EBS-based storage for an OpenSearch Service domain.
+ type: dict
+ suboptions:
+ ebs_enabled:
+ description:
+ - Specifies whether EBS-based storage is enabled.
+ required: false
+ type: bool
+ volume_type:
+ description:
+ - Specifies the volume type for EBS-based storage. "standard"|"gp2"|"io1"
+ required: false
+ type: str
+ volume_size:
+ description:
+ - Integer to specify the size of an EBS volume.
+ required: false
+ type: int
+ iops:
+ description:
+ - The IOPD for a Provisioned IOPS EBS volume (SSD).
+ required: false
+ type: int
+ vpc_options:
+ description:
+ - Options to specify the subnets and security groups for a VPC endpoint.
+ type: dict
+ suboptions:
+ subnets:
+ description:
+ - Specifies the subnet ids for VPC endpoint.
+ required: false
+ type: list
+ elements: str
+ security_groups:
+ description:
+ - Specifies the security group ids for VPC endpoint.
+ required: false
+ type: list
+ elements: str
+ snapshot_options:
+ description:
+ - Option to set time, in UTC format, of the daily automated snapshot.
+ type: dict
+ suboptions:
+ automated_snapshot_start_hour:
+ description:
+ - >
+ Integer value from 0 to 23 specifying when the service takes a daily automated snapshot
+ of the specified Elasticsearch domain.
+ required: false
+ type: int
+ access_policies:
+ description:
+ - IAM access policy as a JSON-formatted string.
+ required: false
+ type: dict
+ encryption_at_rest_options:
+ description:
+ - Parameters to enable encryption at rest.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - Should data be encrypted while at rest.
+ required: false
+ type: bool
+ kms_key_id:
+ description:
+ - If encryption at rest enabled, this identifies the encryption key to use.
+ - The value should be a KMS key ARN. It can also be the KMS key id.
+ required: false
+ type: str
+ node_to_node_encryption_options:
+ description:
+ - Node-to-node encryption options.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - True to enable node-to-node encryption.
+ required: false
+ type: bool
+ cognito_options:
+ description:
+ - Parameters to configure OpenSearch Service to use Amazon Cognito authentication for OpenSearch Dashboards.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - The option to enable Cognito for OpenSearch Dashboards authentication.
+ required: false
+ type: bool
+ user_pool_id:
+ description:
+ - The Cognito user pool ID for OpenSearch Dashboards authentication.
+ required: false
+ type: str
+ identity_pool_id:
+ description:
+ - The Cognito identity pool ID for OpenSearch Dashboards authentication.
+ required: false
+ type: str
+ role_arn:
+ description:
+ - The role ARN that provides OpenSearch permissions for accessing Cognito resources.
+ required: false
+ type: str
+ domain_endpoint_options:
+ description:
+ - Options to specify configuration that will be applied to the domain endpoint.
+ type: dict
+ suboptions:
+ enforce_https:
+ description:
+ - Whether only HTTPS endpoint should be enabled for the domain.
+ type: bool
+ tls_security_policy:
+ description:
+ - Specify the TLS security policy to apply to the HTTPS endpoint of the domain.
+ type: str
+ custom_endpoint_enabled:
+ description:
+ - Whether to enable a custom endpoint for the domain.
+ type: bool
+ custom_endpoint:
+ description:
+ - The fully qualified domain for your custom endpoint.
+ type: str
+ custom_endpoint_certificate_arn:
+ description:
+ - The ACM certificate ARN for your custom endpoint.
+ type: str
+ advanced_security_options:
+ description:
+ - Specifies advanced security options.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - True if advanced security is enabled.
+ - You must enable node-to-node encryption to use advanced security options.
+ type: bool
+ internal_user_database_enabled:
+ description:
+ - True if the internal user database is enabled.
+ type: bool
+ master_user_options:
+ description:
+ - Credentials for the master user, username and password, ARN, or both.
+ type: dict
+ suboptions:
+ master_user_arn:
+ description:
+ - ARN for the master user (if IAM is enabled).
+ type: str
+ master_user_name:
+ description:
+ - The username of the master user, which is stored in the Amazon OpenSearch Service domain internal database.
+ type: str
+ master_user_password:
+ description:
+ - The password of the master user, which is stored in the Amazon OpenSearch Service domain internal database.
+ type: str
+ saml_options:
+ description:
+ - The SAML application configuration for the domain.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - True if SAML is enabled.
+ - To use SAML authentication, you must enable fine-grained access control.
+ - You can only enable SAML authentication for OpenSearch Dashboards on existing domains,
+ not during the creation of new ones.
+ - Domains only support one Dashboards authentication method at a time.
+ If you have Amazon Cognito authentication for OpenSearch Dashboards enabled,
+ you must disable it before you can enable SAML.
+ type: bool
+ idp:
+ description:
+ - The SAML Identity Provider's information.
+ type: dict
+ suboptions:
+ metadata_content:
+ description:
+ - The metadata of the SAML application in XML format.
+ type: str
+ entity_id:
+ description:
+ - The unique entity ID of the application in SAML identity provider.
+ type: str
+ master_user_name:
+ description:
+ - The SAML master username, which is stored in the Amazon OpenSearch Service domain internal database.
+ type: str
+ master_backend_role:
+ description:
+ - The backend role that the SAML master user is mapped to.
+ type: str
+ subject_key:
+ description:
+ - Element of the SAML assertion to use for username. Default is NameID.
+ type: str
+ roles_key:
+ description:
+ - Element of the SAML assertion to use for backend roles. Default is roles.
+ type: str
+ session_timeout_minutes:
+ description:
+ - The duration, in minutes, after which a user session becomes inactive. Acceptable values are between 1 and 1440, and the default value is 60.
+ type: int
+ auto_tune_options:
+ description:
+ - Specifies Auto-Tune options.
+ type: dict
+ suboptions:
+ desired_state:
+ description:
+ - The Auto-Tune desired state. Valid values are ENABLED and DISABLED.
+ type: str
+ choices: ['ENABLED', 'DISABLED']
+ maintenance_schedules:
+ description:
+ - A list of maintenance schedules.
+ type: list
+ elements: dict
+ suboptions:
+ start_at:
+ description:
+ - The timestamp at which the Auto-Tune maintenance schedule starts.
+ type: str
+ duration:
+ description:
+ - Specifies maintenance schedule duration, duration value and duration unit.
+ type: dict
+ suboptions:
+ value:
+ description:
+ - Integer to specify the value of a maintenance schedule duration.
+ type: int
+ unit:
+ description:
+ - The unit of a maintenance schedule duration. Valid value is HOURS.
+ choices: ['HOURS']
+ type: str
+ cron_expression_for_recurrence:
+ description:
+ - A cron expression for a recurring maintenance schedule.
+ type: str
+ wait:
+ description:
+ - Whether or not to wait for completion of OpenSearch creation, modification or deletion.
+ type: bool
+ default: false
+ wait_timeout:
+ description:
+ - how long before wait gives up, in seconds.
+ default: 300
+ type: int
+requirements:
+ - botocore >= 1.21.38
+extends_documentation_fragment:
+ - amazon.aws.aws
+ - amazon.aws.ec2
+ - amazon.aws.boto3
+ - amazon.aws.tags
+"""
+
+EXAMPLES = """
+
+- name: Create OpenSearch domain for dev environment, no zone awareness, no dedicated masters
+ community.aws.opensearch:
+ domain_name: "dev-cluster"
+ engine_version: Elasticsearch_1.1
+ cluster_config:
+ instance_type: "t2.small.search"
+ instance_count: 2
+ zone_awareness: false
+ dedicated_master: false
+ ebs_options:
+ ebs_enabled: true
+ volume_type: "gp2"
+ volume_size: 10
+ access_policies: "{{ lookup('file', 'policy.json') | from_json }}"
+
+- name: Create OpenSearch domain with dedicated masters
+ community.aws.opensearch:
+ domain_name: "my-domain"
+ engine_version: OpenSearch_1.1
+ cluster_config:
+ instance_type: "t2.small.search"
+ instance_count: 12
+ dedicated_master: true
+ zone_awareness: true
+ availability_zone_count: 2
+ dedicated_master_instance_type: "t2.small.search"
+ dedicated_master_instance_count: 3
+ warm_enabled: true
+ warm_type: "ultrawarm1.medium.search"
+ warm_count: 1
+ cold_storage_options:
+ enabled: false
+ ebs_options:
+ ebs_enabled: true
+ volume_type: "io1"
+ volume_size: 10
+ iops: 1000
+ vpc_options:
+ subnets:
+ - "subnet-e537d64a"
+ - "subnet-e537d64b"
+ security_groups:
+ - "sg-dd2f13cb"
+ - "sg-dd2f13cc"
+ snapshot_options:
+ automated_snapshot_start_hour: 13
+ access_policies: "{{ lookup('file', 'policy.json') | from_json }}"
+ encryption_at_rest_options:
+ enabled: false
+ node_to_node_encryption_options:
+ enabled: false
+ auto_tune_options:
+ enabled: true
+ maintenance_schedules:
+ - start_at: "2025-01-12"
+ duration:
+ value: 1
+ unit: "HOURS"
+ cron_expression_for_recurrence: "cron(0 12 * * ? *)"
+ - start_at: "2032-01-12"
+ duration:
+ value: 2
+ unit: "HOURS"
+ cron_expression_for_recurrence: "cron(0 12 * * ? *)"
+ tags:
+ Environment: Development
+ Application: Search
+ wait: true
+
+- name: Increase size of EBS volumes for existing cluster
+ community.aws.opensearch:
+ domain_name: "my-domain"
+ ebs_options:
+ volume_size: 5
+ wait: true
+
+- name: Increase instance count for existing cluster
+ community.aws.opensearch:
+ domain_name: "my-domain"
+ cluster_config:
+ instance_count: 40
+ wait: true
+
+"""
+
+from copy import deepcopy
+import datetime
+import json
+
+try:
+ import botocore
+except ImportError:
+ pass # handled by AnsibleAWSModule
+
+from ansible.module_utils.six import string_types
+
+# import module snippets
+from ansible_collections.amazon.aws.plugins.module_utils.core import (
+ AnsibleAWSModule,
+ is_boto3_error_code,
+)
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import (
+ AWSRetry,
+ boto3_tag_list_to_ansible_dict,
+ compare_policies,
+)
+from ansible_collections.community.aws.plugins.module_utils.opensearch import (
+ compare_domain_versions,
+ ensure_tags,
+ get_domain_status,
+ get_domain_config,
+ get_target_increment_version,
+ normalize_opensearch,
+ parse_version,
+ wait_for_domain_status,
+)
+
+
+def ensure_domain_absent(client, module):
+ domain_name = module.params.get("domain_name")
+ changed = False
+
+ domain = get_domain_status(client, module, domain_name)
+ if module.check_mode:
+ module.exit_json(
+ changed=True, msg="Would have deleted domain if not in check mode"
+ )
+ try:
+ client.delete_domain(DomainName=domain_name)
+ changed = True
+ except is_boto3_error_code("ResourceNotFoundException"):
+ # The resource does not exist, or it has already been deleted
+ return dict(changed=False)
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
+ module.fail_json_aws(e, msg="trying to delete domain")
+
+ # If we're not waiting for a delete to complete then we're all done
+ # so just return
+ if not domain or not module.params.get("wait"):
+ return dict(changed=changed)
+ try:
+ wait_for_domain_status(client, module, domain_name, "domain_deleted")
+ return dict(changed=changed)
+ except is_boto3_error_code("ResourceNotFoundException"):
+ return dict(changed=changed)
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
+ module.fail_json_aws(e, "awaiting domain deletion")
+
+
+def upgrade_domain(client, module, source_version, target_engine_version):
+ domain_name = module.params.get("domain_name")
+ # Determine if it's possible to upgrade directly from source version
+ # to target version, or if it's necessary to upgrade through intermediate major versions.
+ next_version = target_engine_version
+ # When perform_check_only is true, indicates that an upgrade eligibility check needs
+ # to be performed. Does not actually perform the upgrade.
+ perform_check_only = False
+ if module.check_mode:
+ perform_check_only = True
+ current_version = source_version
+ while current_version != target_engine_version:
+ v = get_target_increment_version(client, module, domain_name, target_engine_version)
+ if v is None:
+ # There is no compatible version, according to the get_compatible_versions() API.
+ # The upgrade should fail, but try anyway.
+ next_version = target_engine_version
+ if next_version != target_engine_version:
+ # It's not possible to upgrade directly to the target version.
+ # Check the module parameters to determine if this is allowed or not.
+ if not module.params.get("allow_intermediate_upgrades"):
+ module.fail_json(msg="Cannot upgrade from {0} to version {1}. The highest compatible version is {2}".format(
+ source_version, target_engine_version, next_version))
+
+ parameters = {
+ "DomainName": domain_name,
+ "TargetVersion": next_version,
+ "PerformCheckOnly": perform_check_only,
+ }
+
+ if not module.check_mode:
+ # If background tasks are in progress, wait until they complete.
+ # This can take several hours depending on the cluster size and the type of background tasks
+ # (maybe an upgrade is already in progress).
+ # It's not possible to upgrade a domain that has background tasks are in progress,
+ # the call to client.upgrade_domain would fail.
+ wait_for_domain_status(client, module, domain_name, "domain_available")
+
+ try:
+ client.upgrade_domain(**parameters)
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ # In check mode (=> PerformCheckOnly==True), a ValidationException may be
+ # raised if it's not possible to upgrade to the target version.
+ module.fail_json_aws(
+ e,
+ msg="Couldn't upgrade domain {0} from {1} to {2}".format(
+ domain_name, current_version, next_version
+ ),
+ )
+
+ if module.check_mode:
+ module.exit_json(
+ changed=True,
+ msg="Would have upgraded domain from {0} to {1} if not in check mode".format(
+ current_version, next_version
+ ),
+ )
+ current_version = next_version
+
+ if module.params.get("wait"):
+ wait_for_domain_status(client, module, domain_name, "domain_available")
+
+
+def set_cluster_config(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+
+ cluster_config = desired_domain_config["ClusterConfig"]
+ cluster_opts = module.params.get("cluster_config")
+ if cluster_opts is not None:
+ if cluster_opts.get("instance_type") is not None:
+ cluster_config["InstanceType"] = cluster_opts.get("instance_type")
+ if cluster_opts.get("instance_count") is not None:
+ cluster_config["InstanceCount"] = cluster_opts.get("instance_count")
+ if cluster_opts.get("zone_awareness") is not None:
+ cluster_config["ZoneAwarenessEnabled"] = cluster_opts.get("zone_awareness")
+ if cluster_config["ZoneAwarenessEnabled"]:
+ if cluster_opts.get("availability_zone_count") is not None:
+ cluster_config["ZoneAwarenessConfig"] = {
+ "AvailabilityZoneCount": cluster_opts.get(
+ "availability_zone_count"
+ ),
+ }
+
+ if cluster_opts.get("dedicated_master") is not None:
+ cluster_config["DedicatedMasterEnabled"] = cluster_opts.get(
+ "dedicated_master"
+ )
+ if cluster_config["DedicatedMasterEnabled"]:
+ if cluster_opts.get("dedicated_master_instance_type") is not None:
+ cluster_config["DedicatedMasterType"] = cluster_opts.get(
+ "dedicated_master_instance_type"
+ )
+ if cluster_opts.get("dedicated_master_instance_count") is not None:
+ cluster_config["DedicatedMasterCount"] = cluster_opts.get(
+ "dedicated_master_instance_count"
+ )
+
+ if cluster_opts.get("warm_enabled") is not None:
+ cluster_config["WarmEnabled"] = cluster_opts.get("warm_enabled")
+ if cluster_config["WarmEnabled"]:
+ if cluster_opts.get("warm_type") is not None:
+ cluster_config["WarmType"] = cluster_opts.get("warm_type")
+ if cluster_opts.get("warm_count") is not None:
+ cluster_config["WarmCount"] = cluster_opts.get("warm_count")
+
+ cold_storage_opts = None
+ if cluster_opts is not None:
+ cold_storage_opts = cluster_opts.get("cold_storage_options")
+ if compare_domain_versions(desired_domain_config["EngineVersion"], "Elasticsearch_7.9") < 0:
+ # If the engine version is ElasticSearch < 7.9, cold storage is not supported.
+ # When querying a domain < 7.9, the AWS API indicates cold storage is disabled (Enabled: False),
+ # which makes sense. However, trying to do HTTP POST with Enable: False causes an API error.
+ # The 'ColdStorageOptions' attribute should not be present in HTTP POST.
+ if cold_storage_opts is not None and cold_storage_opts.get("enabled"):
+ module.fail_json(msg="Cold Storage is not supported")
+ cluster_config.pop("ColdStorageOptions", None)
+ if (
+ current_domain_config is not None
+ and "ClusterConfig" in current_domain_config
+ ):
+ # Remove 'ColdStorageOptions' from the current domain config, otherwise the actual vs desired diff
+ # will indicate a change must be done.
+ current_domain_config["ClusterConfig"].pop("ColdStorageOptions", None)
+ else:
+ # Elasticsearch 7.9 and above support ColdStorageOptions.
+ if (
+ cold_storage_opts is not None
+ and cold_storage_opts.get("enabled") is not None
+ ):
+ cluster_config["ColdStorageOptions"] = {
+ "Enabled": cold_storage_opts.get("enabled"),
+ }
+
+ if (
+ current_domain_config is not None
+ and current_domain_config["ClusterConfig"] != cluster_config
+ ):
+ change_set.append(
+ "ClusterConfig changed from {0} to {1}".format(
+ current_domain_config["ClusterConfig"], cluster_config
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_ebs_options(module, current_domain_config, desired_domain_config, change_set):
+ changed = False
+ ebs_config = desired_domain_config["EBSOptions"]
+ ebs_opts = module.params.get("ebs_options")
+ if ebs_opts is None:
+ return changed
+ if ebs_opts.get("ebs_enabled") is not None:
+ ebs_config["EBSEnabled"] = ebs_opts.get("ebs_enabled")
+
+ if not ebs_config["EBSEnabled"]:
+ desired_domain_config["EBSOptions"] = {
+ "EBSEnabled": False,
+ }
+ else:
+ if ebs_opts.get("volume_type") is not None:
+ ebs_config["VolumeType"] = ebs_opts.get("volume_type")
+ if ebs_opts.get("volume_size") is not None:
+ ebs_config["VolumeSize"] = ebs_opts.get("volume_size")
+ if ebs_opts.get("iops") is not None:
+ ebs_config["Iops"] = ebs_opts.get("iops")
+
+ if (
+ current_domain_config is not None
+ and current_domain_config["EBSOptions"] != ebs_config
+ ):
+ change_set.append(
+ "EBSOptions changed from {0} to {1}".format(
+ current_domain_config["EBSOptions"], ebs_config
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_encryption_at_rest_options(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+ encryption_at_rest_config = desired_domain_config["EncryptionAtRestOptions"]
+ encryption_at_rest_opts = module.params.get("encryption_at_rest_options")
+ if encryption_at_rest_opts is None:
+ return False
+ if encryption_at_rest_opts.get("enabled") is not None:
+ encryption_at_rest_config["Enabled"] = encryption_at_rest_opts.get("enabled")
+ if not encryption_at_rest_config["Enabled"]:
+ desired_domain_config["EncryptionAtRestOptions"] = {
+ "Enabled": False,
+ }
+ else:
+ if encryption_at_rest_opts.get("kms_key_id") is not None:
+ encryption_at_rest_config["KmsKeyId"] = encryption_at_rest_opts.get(
+ "kms_key_id"
+ )
+
+ if (
+ current_domain_config is not None
+ and current_domain_config["EncryptionAtRestOptions"]
+ != encryption_at_rest_config
+ ):
+ change_set.append(
+ "EncryptionAtRestOptions changed from {0} to {1}".format(
+ current_domain_config["EncryptionAtRestOptions"],
+ encryption_at_rest_config,
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_node_to_node_encryption_options(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+ node_to_node_encryption_config = desired_domain_config[
+ "NodeToNodeEncryptionOptions"
+ ]
+ node_to_node_encryption_opts = module.params.get("node_to_node_encryption_options")
+ if node_to_node_encryption_opts is None:
+ return changed
+ if node_to_node_encryption_opts.get("enabled") is not None:
+ node_to_node_encryption_config["Enabled"] = node_to_node_encryption_opts.get(
+ "enabled"
+ )
+
+ if (
+ current_domain_config is not None
+ and current_domain_config["NodeToNodeEncryptionOptions"]
+ != node_to_node_encryption_config
+ ):
+ change_set.append(
+ "NodeToNodeEncryptionOptions changed from {0} to {1}".format(
+ current_domain_config["NodeToNodeEncryptionOptions"],
+ node_to_node_encryption_config,
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_vpc_options(module, current_domain_config, desired_domain_config, change_set):
+ changed = False
+ vpc_config = None
+ if "VPCOptions" in desired_domain_config:
+ vpc_config = desired_domain_config["VPCOptions"]
+ vpc_opts = module.params.get("vpc_options")
+ if vpc_opts is None:
+ return changed
+ vpc_subnets = vpc_opts.get("subnets")
+ if vpc_subnets is not None:
+ if vpc_config is None:
+ vpc_config = {}
+ desired_domain_config["VPCOptions"] = vpc_config
+ # OpenSearch cluster is attached to VPC
+ if isinstance(vpc_subnets, string_types):
+ vpc_subnets = [x.strip() for x in vpc_subnets.split(",")]
+ vpc_config["SubnetIds"] = vpc_subnets
+
+ vpc_security_groups = vpc_opts.get("security_groups")
+ if vpc_security_groups is not None:
+ if vpc_config is None:
+ vpc_config = {}
+ desired_domain_config["VPCOptions"] = vpc_config
+ if isinstance(vpc_security_groups, string_types):
+ vpc_security_groups = [x.strip() for x in vpc_security_groups.split(",")]
+ vpc_config["SecurityGroupIds"] = vpc_security_groups
+
+ if current_domain_config is not None:
+ # Modify existing cluster.
+ current_cluster_is_vpc = False
+ desired_cluster_is_vpc = False
+ if (
+ "VPCOptions" in current_domain_config
+ and "SubnetIds" in current_domain_config["VPCOptions"]
+ and len(current_domain_config["VPCOptions"]["SubnetIds"]) > 0
+ ):
+ current_cluster_is_vpc = True
+ if (
+ "VPCOptions" in desired_domain_config
+ and "SubnetIds" in desired_domain_config["VPCOptions"]
+ and len(desired_domain_config["VPCOptions"]["SubnetIds"]) > 0
+ ):
+ desired_cluster_is_vpc = True
+ if current_cluster_is_vpc != desired_cluster_is_vpc:
+ # AWS does not allow changing the type. Don't fail here so we return the AWS API error.
+ change_set.append("VPCOptions changed between Internet and VPC")
+ changed = True
+ elif desired_cluster_is_vpc is False:
+ # There are no VPCOptions to configure.
+ pass
+ else:
+ # Note the subnets may be the same but be listed in a different order.
+ if set(current_domain_config["VPCOptions"]["SubnetIds"]) != set(
+ vpc_config["SubnetIds"]
+ ):
+ change_set.append(
+ "SubnetIds changed from {0} to {1}".format(
+ current_domain_config["VPCOptions"]["SubnetIds"],
+ vpc_config["SubnetIds"],
+ )
+ )
+ changed = True
+ if set(current_domain_config["VPCOptions"]["SecurityGroupIds"]) != set(
+ vpc_config["SecurityGroupIds"]
+ ):
+ change_set.append(
+ "SecurityGroup changed from {0} to {1}".format(
+ current_domain_config["VPCOptions"]["SecurityGroupIds"],
+ vpc_config["SecurityGroupIds"],
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_snapshot_options(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+ snapshot_config = desired_domain_config["SnapshotOptions"]
+ snapshot_opts = module.params.get("snapshot_options")
+ if snapshot_opts is None:
+ return changed
+ if snapshot_opts.get("automated_snapshot_start_hour") is not None:
+ snapshot_config["AutomatedSnapshotStartHour"] = snapshot_opts.get(
+ "automated_snapshot_start_hour"
+ )
+ if (
+ current_domain_config is not None
+ and current_domain_config["SnapshotOptions"] != snapshot_config
+ ):
+ change_set.append("SnapshotOptions changed")
+ changed = True
+ return changed
+
+
+def set_cognito_options(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+ cognito_config = desired_domain_config["CognitoOptions"]
+ cognito_opts = module.params.get("cognito_options")
+ if cognito_opts is None:
+ return changed
+ if cognito_opts.get("enabled") is not None:
+ cognito_config["Enabled"] = cognito_opts.get("enabled")
+ if not cognito_config["Enabled"]:
+ desired_domain_config["CognitoOptions"] = {
+ "Enabled": False,
+ }
+ else:
+ if cognito_opts.get("cognito_user_pool_id") is not None:
+ cognito_config["UserPoolId"] = cognito_opts.get("cognito_user_pool_id")
+ if cognito_opts.get("cognito_identity_pool_id") is not None:
+ cognito_config["IdentityPoolId"] = cognito_opts.get(
+ "cognito_identity_pool_id"
+ )
+ if cognito_opts.get("cognito_role_arn") is not None:
+ cognito_config["RoleArn"] = cognito_opts.get("cognito_role_arn")
+
+ if (
+ current_domain_config is not None
+ and current_domain_config["CognitoOptions"] != cognito_config
+ ):
+ change_set.append(
+ "CognitoOptions changed from {0} to {1}".format(
+ current_domain_config["CognitoOptions"], cognito_config
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_advanced_security_options(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+ advanced_security_config = desired_domain_config["AdvancedSecurityOptions"]
+ advanced_security_opts = module.params.get("advanced_security_options")
+ if advanced_security_opts is None:
+ return changed
+ if advanced_security_opts.get("enabled") is not None:
+ advanced_security_config["Enabled"] = advanced_security_opts.get("enabled")
+ if not advanced_security_config["Enabled"]:
+ desired_domain_config["AdvancedSecurityOptions"] = {
+ "Enabled": False,
+ }
+ else:
+ if advanced_security_opts.get("internal_user_database_enabled") is not None:
+ advanced_security_config[
+ "InternalUserDatabaseEnabled"
+ ] = advanced_security_opts.get("internal_user_database_enabled")
+ master_user_opts = advanced_security_opts.get("master_user_options")
+ if master_user_opts is not None:
+ advanced_security_config.setdefault("MasterUserOptions", {})
+ if master_user_opts.get("master_user_arn") is not None:
+ advanced_security_config["MasterUserOptions"][
+ "MasterUserARN"
+ ] = master_user_opts.get("master_user_arn")
+ if master_user_opts.get("master_user_name") is not None:
+ advanced_security_config["MasterUserOptions"][
+ "MasterUserName"
+ ] = master_user_opts.get("master_user_name")
+ if master_user_opts.get("master_user_password") is not None:
+ advanced_security_config["MasterUserOptions"][
+ "MasterUserPassword"
+ ] = master_user_opts.get("master_user_password")
+ saml_opts = advanced_security_opts.get("saml_options")
+ if saml_opts is not None:
+ if saml_opts.get("enabled") is not None:
+ advanced_security_config["SamlOptions"]["Enabled"] = saml_opts.get(
+ "enabled"
+ )
+ idp_opts = saml_opts.get("idp")
+ if idp_opts is not None:
+ if idp_opts.get("metadata_content") is not None:
+ advanced_security_config["SamlOptions"]["Idp"][
+ "MetadataContent"
+ ] = idp_opts.get("metadata_content")
+ if idp_opts.get("entity_id") is not None:
+ advanced_security_config["SamlOptions"]["Idp"][
+ "EntityId"
+ ] = idp_opts.get("entity_id")
+ if saml_opts.get("master_user_name") is not None:
+ advanced_security_config["SamlOptions"][
+ "MasterUserName"
+ ] = saml_opts.get("master_user_name")
+ if saml_opts.get("master_backend_role") is not None:
+ advanced_security_config["SamlOptions"][
+ "MasterBackendRole"
+ ] = saml_opts.get("master_backend_role")
+ if saml_opts.get("subject_key") is not None:
+ advanced_security_config["SamlOptions"]["SubjectKey"] = saml_opts.get(
+ "subject_key"
+ )
+ if saml_opts.get("roles_key") is not None:
+ advanced_security_config["SamlOptions"]["RolesKey"] = saml_opts.get(
+ "roles_key"
+ )
+ if saml_opts.get("session_timeout_minutes") is not None:
+ advanced_security_config["SamlOptions"][
+ "SessionTimeoutMinutes"
+ ] = saml_opts.get("session_timeout_minutes")
+
+ if (
+ current_domain_config is not None
+ and current_domain_config["AdvancedSecurityOptions"] != advanced_security_config
+ ):
+ change_set.append(
+ "AdvancedSecurityOptions changed from {0} to {1}".format(
+ current_domain_config["AdvancedSecurityOptions"],
+ advanced_security_config,
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_domain_endpoint_options(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+ domain_endpoint_config = desired_domain_config["DomainEndpointOptions"]
+ domain_endpoint_opts = module.params.get("domain_endpoint_options")
+ if domain_endpoint_opts is None:
+ return changed
+ if domain_endpoint_opts.get("enforce_https") is not None:
+ domain_endpoint_config["EnforceHTTPS"] = domain_endpoint_opts.get(
+ "enforce_https"
+ )
+ if domain_endpoint_opts.get("tls_security_policy") is not None:
+ domain_endpoint_config["TLSSecurityPolicy"] = domain_endpoint_opts.get(
+ "tls_security_policy"
+ )
+ if domain_endpoint_opts.get("custom_endpoint_enabled") is not None:
+ domain_endpoint_config["CustomEndpointEnabled"] = domain_endpoint_opts.get(
+ "custom_endpoint_enabled"
+ )
+ if domain_endpoint_config["CustomEndpointEnabled"]:
+ if domain_endpoint_opts.get("custom_endpoint") is not None:
+ domain_endpoint_config["CustomEndpoint"] = domain_endpoint_opts.get(
+ "custom_endpoint"
+ )
+ if domain_endpoint_opts.get("custom_endpoint_certificate_arn") is not None:
+ domain_endpoint_config[
+ "CustomEndpointCertificateArn"
+ ] = domain_endpoint_opts.get("custom_endpoint_certificate_arn")
+
+ if (
+ current_domain_config is not None
+ and current_domain_config["DomainEndpointOptions"] != domain_endpoint_config
+ ):
+ change_set.append(
+ "DomainEndpointOptions changed from {0} to {1}".format(
+ current_domain_config["DomainEndpointOptions"], domain_endpoint_config
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_auto_tune_options(
+ module, current_domain_config, desired_domain_config, change_set
+):
+ changed = False
+ auto_tune_config = desired_domain_config["AutoTuneOptions"]
+ auto_tune_opts = module.params.get("auto_tune_options")
+ if auto_tune_opts is None:
+ return changed
+ schedules = auto_tune_opts.get("maintenance_schedules")
+ if auto_tune_opts.get("desired_state") is not None:
+ auto_tune_config["DesiredState"] = auto_tune_opts.get("desired_state")
+ if auto_tune_config["DesiredState"] != "ENABLED":
+ desired_domain_config["AutoTuneOptions"] = {
+ "DesiredState": "DISABLED",
+ }
+ elif schedules is not None:
+ auto_tune_config["MaintenanceSchedules"] = []
+ for s in schedules:
+ schedule_entry = {}
+ start_at = s.get("start_at")
+ if start_at is not None:
+ if isinstance(start_at, datetime.datetime):
+ # The property was parsed from yaml to datetime, but the AWS API wants a string
+ start_at = start_at.strftime("%Y-%m-%d")
+ schedule_entry["StartAt"] = start_at
+ duration_opt = s.get("duration")
+ if duration_opt is not None:
+ schedule_entry["Duration"] = {}
+ if duration_opt.get("value") is not None:
+ schedule_entry["Duration"]["Value"] = duration_opt.get("value")
+ if duration_opt.get("unit") is not None:
+ schedule_entry["Duration"]["Unit"] = duration_opt.get("unit")
+ if s.get("cron_expression_for_recurrence") is not None:
+ schedule_entry["CronExpressionForRecurrence"] = s.get(
+ "cron_expression_for_recurrence"
+ )
+ auto_tune_config["MaintenanceSchedules"].append(schedule_entry)
+ if current_domain_config is not None:
+ if (
+ current_domain_config["AutoTuneOptions"]["DesiredState"]
+ != auto_tune_config["DesiredState"]
+ ):
+ change_set.append(
+ "AutoTuneOptions.DesiredState changed from {0} to {1}".format(
+ current_domain_config["AutoTuneOptions"]["DesiredState"],
+ auto_tune_config["DesiredState"],
+ )
+ )
+ changed = True
+ if (
+ auto_tune_config["MaintenanceSchedules"]
+ != current_domain_config["AutoTuneOptions"]["MaintenanceSchedules"]
+ ):
+ change_set.append(
+ "AutoTuneOptions.MaintenanceSchedules changed from {0} to {1}".format(
+ current_domain_config["AutoTuneOptions"]["MaintenanceSchedules"],
+ auto_tune_config["MaintenanceSchedules"],
+ )
+ )
+ changed = True
+ return changed
+
+
+def set_access_policy(module, current_domain_config, desired_domain_config, change_set):
+ access_policy_config = None
+ changed = False
+ access_policy_opt = module.params.get("access_policies")
+ if access_policy_opt is None:
+ return changed
+ try:
+ access_policy_config = json.dumps(access_policy_opt)
+ except Exception as e:
+ module.fail_json(
+ msg="Failed to convert the policy into valid JSON: %s" % str(e)
+ )
+ if current_domain_config is not None:
+ # Updating existing domain
+ current_access_policy = json.loads(current_domain_config["AccessPolicies"])
+ if not compare_policies(current_access_policy, access_policy_opt):
+ change_set.append(
+ "AccessPolicy changed from {0} to {1}".format(
+ current_access_policy, access_policy_opt
+ )
+ )
+ changed = True
+ desired_domain_config["AccessPolicies"] = access_policy_config
+ else:
+ # Creating new domain
+ desired_domain_config["AccessPolicies"] = access_policy_config
+ return changed
+
+
+def ensure_domain_present(client, module):
+ domain_name = module.params.get("domain_name")
+
+ # Create default if OpenSearch does not exist. If domain already exists,
+ # the data is populated by retrieving the current configuration from the API.
+ desired_domain_config = {
+ "DomainName": module.params.get("domain_name"),
+ "EngineVersion": "OpenSearch_1.1",
+ "ClusterConfig": {
+ "InstanceType": "t2.small.search",
+ "InstanceCount": 2,
+ "ZoneAwarenessEnabled": False,
+ "DedicatedMasterEnabled": False,
+ "WarmEnabled": False,
+ },
+ # By default create ES attached to the Internet.
+ # If the "VPCOptions" property is specified, even if empty, the API server interprets
+ # as incomplete VPC configuration.
+ # "VPCOptions": {},
+ "EBSOptions": {
+ "EBSEnabled": False,
+ },
+ "EncryptionAtRestOptions": {
+ "Enabled": False,
+ },
+ "NodeToNodeEncryptionOptions": {
+ "Enabled": False,
+ },
+ "SnapshotOptions": {
+ "AutomatedSnapshotStartHour": 0,
+ },
+ "CognitoOptions": {
+ "Enabled": False,
+ },
+ "AdvancedSecurityOptions": {
+ "Enabled": False,
+ },
+ "DomainEndpointOptions": {
+ "CustomEndpointEnabled": False,
+ },
+ "AutoTuneOptions": {
+ "DesiredState": "DISABLED",
+ },
+ }
+ # Determine if OpenSearch domain already exists.
+ # current_domain_config may be None if the domain does not exist.
+ (current_domain_config, domain_arn) = get_domain_config(client, module, domain_name)
+ if current_domain_config is not None:
+ desired_domain_config = deepcopy(current_domain_config)
+
+ if module.params.get("engine_version") is not None:
+ # Validate the engine_version
+ v = parse_version(module.params.get("engine_version"))
+ if v is None:
+ module.fail_json(
+ "Invalid engine_version. Must be Elasticsearch_X.Y or OpenSearch_X.Y"
+ )
+ desired_domain_config["EngineVersion"] = module.params.get("engine_version")
+
+ changed = False
+ change_set = [] # For check mode purpose
+
+ changed |= set_cluster_config(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_ebs_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_encryption_at_rest_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_node_to_node_encryption_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_vpc_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_snapshot_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_cognito_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_advanced_security_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_domain_endpoint_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_auto_tune_options(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+ changed |= set_access_policy(
+ module, current_domain_config, desired_domain_config, change_set
+ )
+
+ if current_domain_config is not None:
+ if (
+ desired_domain_config["EngineVersion"]
+ != current_domain_config["EngineVersion"]
+ ):
+ changed = True
+ change_set.append("EngineVersion changed")
+ upgrade_domain(
+ client,
+ module,
+ current_domain_config["EngineVersion"],
+ desired_domain_config["EngineVersion"],
+ )
+
+ if changed:
+ if module.check_mode:
+ module.exit_json(
+ changed=True,
+ msg=f"Would have updated domain if not in check mode: {change_set}",
+ )
+ # Remove the "EngineVersion" attribute, the AWS API does not accept this attribute.
+ desired_domain_config.pop("EngineVersion", None)
+ try:
+ client.update_domain_config(**desired_domain_config)
+ except (
+ botocore.exceptions.BotoCoreError,
+ botocore.exceptions.ClientError,
+ ) as e:
+ module.fail_json_aws(
+ e, msg="Couldn't update domain {0}".format(domain_name)
+ )
+
+ else:
+ # Create new OpenSearch cluster
+ if module.params.get("access_policies") is None:
+ module.fail_json(
+ "state is present but the following is missing: access_policies"
+ )
+
+ changed = True
+ if module.check_mode:
+ module.exit_json(
+ changed=True, msg="Would have created a domain if not in check mode"
+ )
+ try:
+ response = client.create_domain(**desired_domain_config)
+ domain = response["DomainStatus"]
+ domain_arn = domain["ARN"]
+ except (
+ botocore.exceptions.BotoCoreError,
+ botocore.exceptions.ClientError,
+ ) as e:
+ module.fail_json_aws(
+ e, msg="Couldn't update domain {0}".format(domain_name)
+ )
+
+ try:
+ existing_tags = boto3_tag_list_to_ansible_dict(
+ client.list_tags(ARN=domain_arn, aws_retry=True)["TagList"]
+ )
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
+ module.fail_json_aws(e, "Couldn't get tags for domain %s" % domain_name)
+
+ desired_tags = module.params["tags"]
+ purge_tags = module.params["purge_tags"]
+ changed |= ensure_tags(
+ client, module, domain_arn, existing_tags, desired_tags, purge_tags
+ )
+
+ if module.params.get("wait") and not module.check_mode:
+ wait_for_domain_status(client, module, domain_name, "domain_available")
+
+ domain = get_domain_status(client, module, domain_name)
+
+ return dict(changed=changed, **normalize_opensearch(client, module, domain))
+
+
+def main():
+
+ module = AnsibleAWSModule(
+ argument_spec=dict(
+ state=dict(choices=["present", "absent"], default="present"),
+ domain_name=dict(required=True),
+ engine_version=dict(),
+ allow_intermediate_upgrades=dict(required=False, type="bool", default=True),
+ access_policies=dict(required=False, type="dict"),
+ cluster_config=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ instance_type=dict(),
+ instance_count=dict(required=False, type="int"),
+ zone_awareness=dict(required=False, type="bool"),
+ availability_zone_count=dict(required=False, type="int"),
+ dedicated_master=dict(required=False, type="bool"),
+ dedicated_master_instance_type=dict(),
+ dedicated_master_instance_count=dict(type="int"),
+ warm_enabled=dict(required=False, type="bool"),
+ warm_type=dict(required=False),
+ warm_count=dict(required=False, type="int"),
+ cold_storage_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ enabled=dict(required=False, type="bool"),
+ ),
+ ),
+ ),
+ ),
+ snapshot_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ automated_snapshot_start_hour=dict(required=False, type="int"),
+ ),
+ ),
+ ebs_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ ebs_enabled=dict(required=False, type="bool"),
+ volume_type=dict(required=False),
+ volume_size=dict(required=False, type="int"),
+ iops=dict(required=False, type="int"),
+ ),
+ ),
+ vpc_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ subnets=dict(type="list", elements="str", required=False),
+ security_groups=dict(type="list", elements="str", required=False),
+ ),
+ ),
+ cognito_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ enabled=dict(required=False, type="bool"),
+ user_pool_id=dict(required=False),
+ identity_pool_id=dict(required=False),
+ role_arn=dict(required=False, no_log=False),
+ ),
+ ),
+ encryption_at_rest_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ enabled=dict(type="bool"),
+ kms_key_id=dict(required=False),
+ ),
+ ),
+ node_to_node_encryption_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ enabled=dict(type="bool"),
+ ),
+ ),
+ domain_endpoint_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ enforce_https=dict(type="bool"),
+ tls_security_policy=dict(),
+ custom_endpoint_enabled=dict(type="bool"),
+ custom_endpoint=dict(),
+ custom_endpoint_certificate_arn=dict(),
+ ),
+ ),
+ advanced_security_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ enabled=dict(type="bool"),
+ internal_user_database_enabled=dict(type="bool"),
+ master_user_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ master_user_arn=dict(),
+ master_user_name=dict(),
+ master_user_password=dict(no_log=True),
+ ),
+ ),
+ saml_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ enabled=dict(type="bool"),
+ idp=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ metadata_content=dict(),
+ entity_id=dict(),
+ ),
+ ),
+ master_user_name=dict(),
+ master_backend_role=dict(),
+ subject_key=dict(no_log=False),
+ roles_key=dict(no_log=False),
+ session_timeout_minutes=dict(type="int"),
+ ),
+ ),
+ ),
+ ),
+ auto_tune_options=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ desired_state=dict(choices=["ENABLED", "DISABLED"]),
+ maintenance_schedules=dict(
+ type="list",
+ elements="dict",
+ default=None,
+ options=dict(
+ start_at=dict(),
+ duration=dict(
+ type="dict",
+ default=None,
+ options=dict(
+ value=dict(type="int"),
+ unit=dict(choices=["HOURS"]),
+ ),
+ ),
+ cron_expression_for_recurrence=dict(),
+ ),
+ ),
+ ),
+ ),
+ tags=dict(type="dict", aliases=["resource_tags"]),
+ purge_tags=dict(type="bool", default=True),
+ wait=dict(type="bool", default=False),
+ wait_timeout=dict(type="int", default=300),
+ ),
+ supports_check_mode=True,
+ )
+
+ module.require_botocore_at_least("1.21.38")
+
+ try:
+ client = module.client("opensearch", retry_decorator=AWSRetry.jittered_backoff())
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
+ module.fail_json_aws(e, msg="Failed to connect to AWS opensearch service")
+
+ if module.params["state"] == "absent":
+ ret_dict = ensure_domain_absent(client, module)
+ else:
+ ret_dict = ensure_domain_present(client, module)
+
+ module.exit_json(**ret_dict)
+
+
+if __name__ == "__main__":
+ main()