summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/aws/plugins/modules/redshift.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/community/aws/plugins/modules/redshift.py
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/aws/plugins/modules/redshift.py')
-rw-r--r--ansible_collections/community/aws/plugins/modules/redshift.py673
1 files changed, 673 insertions, 0 deletions
diff --git a/ansible_collections/community/aws/plugins/modules/redshift.py b/ansible_collections/community/aws/plugins/modules/redshift.py
new file mode 100644
index 000000000..27e959893
--- /dev/null
+++ b/ansible_collections/community/aws/plugins/modules/redshift.py
@@ -0,0 +1,673 @@
+#!/usr/bin/python
+
+# Copyright 2014 Jens Carl, Hothead Games Inc.
+# 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 = r'''
+---
+author:
+ - "Jens Carl (@j-carl), Hothead Games Inc."
+ - "Rafael Driutti (@rafaeldriutti)"
+module: redshift
+version_added: 1.0.0
+short_description: create, delete, or modify an Amazon Redshift instance
+description:
+ - Creates, deletes, or modifies Amazon Redshift cluster instances.
+options:
+ command:
+ description:
+ - Specifies the action to take.
+ required: true
+ choices: [ 'create', 'facts', 'delete', 'modify' ]
+ type: str
+ identifier:
+ description:
+ - Redshift cluster identifier.
+ required: true
+ type: str
+ node_type:
+ description:
+ - The node type of the cluster.
+ - Require when I(command=create).
+ choices: ['ds1.xlarge', 'ds1.8xlarge', 'ds2.xlarge', 'ds2.8xlarge', 'dc1.large','dc2.large',
+ 'dc1.8xlarge', 'dw1.xlarge', 'dw1.8xlarge', 'dw2.large', 'dw2.8xlarge']
+ type: str
+ username:
+ description:
+ - Master database username.
+ - Used only when I(command=create).
+ type: str
+ password:
+ description:
+ - Master database password.
+ - Used only when I(command=create).
+ type: str
+ cluster_type:
+ description:
+ - The type of cluster.
+ choices: ['multi-node', 'single-node' ]
+ default: 'single-node'
+ type: str
+ db_name:
+ description:
+ - Name of the database.
+ type: str
+ availability_zone:
+ description:
+ - Availability zone in which to launch cluster.
+ aliases: ['zone', 'aws_zone']
+ type: str
+ number_of_nodes:
+ description:
+ - Number of nodes.
+ - Only used when I(cluster_type=multi-node).
+ type: int
+ cluster_subnet_group_name:
+ description:
+ - Which subnet to place the cluster.
+ aliases: ['subnet']
+ type: str
+ cluster_security_groups:
+ description:
+ - In which security group the cluster belongs.
+ type: list
+ elements: str
+ aliases: ['security_groups']
+ vpc_security_group_ids:
+ description:
+ - VPC security group
+ aliases: ['vpc_security_groups']
+ type: list
+ elements: str
+ skip_final_cluster_snapshot:
+ description:
+ - Skip a final snapshot before deleting the cluster.
+ - Used only when I(command=delete).
+ aliases: ['skip_final_snapshot']
+ default: false
+ type: bool
+ final_cluster_snapshot_identifier:
+ description:
+ - Identifier of the final snapshot to be created before deleting the cluster.
+ - If this parameter is provided, I(skip_final_cluster_snapshot) must be C(false).
+ - Used only when I(command=delete).
+ aliases: ['final_snapshot_id']
+ type: str
+ preferred_maintenance_window:
+ description:
+ - 'Maintenance window in format of C(ddd:hh24:mi-ddd:hh24:mi). (Example: C(Mon:22:00-Mon:23:15))'
+ - Times are specified in UTC.
+ - If not specified then a random 30 minute maintenance window is assigned.
+ aliases: ['maintance_window', 'maint_window']
+ type: str
+ cluster_parameter_group_name:
+ description:
+ - Name of the cluster parameter group.
+ aliases: ['param_group_name']
+ type: str
+ automated_snapshot_retention_period:
+ description:
+ - The number of days that automated snapshots are retained.
+ aliases: ['retention_period']
+ type: int
+ port:
+ description:
+ - Which port the cluster is listening on.
+ type: int
+ cluster_version:
+ description:
+ - Which version the cluster should have.
+ aliases: ['version']
+ choices: ['1.0']
+ type: str
+ allow_version_upgrade:
+ description:
+ - When I(allow_version_upgrade=true) the cluster may be automatically
+ upgraded during the maintenance window.
+ aliases: ['version_upgrade']
+ default: true
+ type: bool
+ publicly_accessible:
+ description:
+ - If the cluster is accessible publicly or not.
+ default: false
+ type: bool
+ encrypted:
+ description:
+ - If the cluster is encrypted or not.
+ default: false
+ type: bool
+ elastic_ip:
+ description:
+ - An Elastic IP to use for the cluster.
+ type: str
+ new_cluster_identifier:
+ description:
+ - Only used when command=modify.
+ aliases: ['new_identifier']
+ type: str
+ wait:
+ description:
+ - When I(command=create), I(command=modify) or I(command=restore) then wait for the database to enter the 'available' state.
+ - When I(command=delete) wait for the database to be terminated.
+ type: bool
+ default: false
+ wait_timeout:
+ description:
+ - When I(wait=true) defines how long in seconds before giving up.
+ default: 300
+ type: int
+ enhanced_vpc_routing:
+ description:
+ - Whether the cluster should have enhanced VPC routing enabled.
+ default: false
+ type: bool
+notes:
+ - Support for I(tags) and I(purge_tags) was added in release 1.3.0.
+extends_documentation_fragment:
+ - amazon.aws.aws
+ - amazon.aws.ec2
+ - amazon.aws.boto3
+ - amazon.aws.tags
+'''
+
+EXAMPLES = r'''
+- name: Basic cluster provisioning example
+ community.aws.redshift:
+ command: create
+ node_type: ds1.xlarge
+ identifier: new_cluster
+ username: cluster_admin
+ password: 1nsecure
+
+- name: Cluster delete example
+ community.aws.redshift:
+ command: delete
+ identifier: new_cluster
+ skip_final_cluster_snapshot: true
+ wait: true
+'''
+
+RETURN = r'''
+cluster:
+ description: dictionary containing all the cluster information
+ returned: success
+ type: complex
+ contains:
+ identifier:
+ description: Id of the cluster.
+ returned: success
+ type: str
+ sample: "new_redshift_cluster"
+ create_time:
+ description: Time of the cluster creation as timestamp.
+ returned: success
+ type: float
+ sample: 1430158536.308
+ status:
+ description: Status of the cluster.
+ returned: success
+ type: str
+ sample: "available"
+ db_name:
+ description: Name of the database.
+ returned: success
+ type: str
+ sample: "new_db_name"
+ availability_zone:
+ description: Amazon availability zone where the cluster is located. "None" until cluster is available.
+ returned: success
+ type: str
+ sample: "us-east-1b"
+ maintenance_window:
+ description: Time frame when maintenance/upgrade are done.
+ returned: success
+ type: str
+ sample: "sun:09:30-sun:10:00"
+ private_ip_address:
+ description: Private IP address of the main node.
+ returned: success
+ type: str
+ sample: "10.10.10.10"
+ public_ip_address:
+ description: Public IP address of the main node. "None" when enhanced_vpc_routing is enabled.
+ returned: success
+ type: str
+ sample: "0.0.0.0"
+ port:
+ description: Port of the cluster. "None" until cluster is available.
+ returned: success
+ type: int
+ sample: 5439
+ url:
+ description: FQDN of the main cluster node. "None" until cluster is available.
+ returned: success
+ type: str
+ sample: "new-redshift_cluster.jfkdjfdkj.us-east-1.redshift.amazonaws.com"
+ enhanced_vpc_routing:
+ description: status of the enhanced vpc routing feature.
+ returned: success
+ type: bool
+ tags:
+ description: aws tags for cluster.
+ returned: success
+ type: dict
+'''
+
+try:
+ import botocore
+except ImportError:
+ pass # caught by AnsibleAWSModule
+
+from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
+from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags
+from ansible_collections.amazon.aws.plugins.module_utils.ec2 import snake_dict_to_camel_dict
+from ansible_collections.amazon.aws.plugins.module_utils.iam import get_aws_account_id
+
+
+def _ensure_tags(redshift, identifier, existing_tags, module):
+ """Compares and update resource tags"""
+
+ account_id = get_aws_account_id(module)
+ region = module.params.get('region')
+ resource_arn = "arn:aws:redshift:{0}:{1}:cluster:{2}" .format(region, account_id, identifier)
+ tags = module.params.get('tags')
+ purge_tags = module.params.get('purge_tags')
+
+ tags_to_add, tags_to_remove = compare_aws_tags(boto3_tag_list_to_ansible_dict(existing_tags), tags, purge_tags)
+
+ if tags_to_add:
+ try:
+ redshift.create_tags(ResourceName=resource_arn, Tags=ansible_dict_to_boto3_tag_list(tags_to_add))
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Failed to add tags to cluster")
+
+ if tags_to_remove:
+ try:
+ redshift.delete_tags(ResourceName=resource_arn, TagKeys=tags_to_remove)
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Failed to delete tags on cluster")
+
+ changed = bool(tags_to_add or tags_to_remove)
+ return changed
+
+
+def _collect_facts(resource):
+ """Transform cluster information to dict."""
+ facts = {
+ 'identifier': resource['ClusterIdentifier'],
+ 'status': resource['ClusterStatus'],
+ 'username': resource['MasterUsername'],
+ 'db_name': resource['DBName'],
+ 'maintenance_window': resource['PreferredMaintenanceWindow'],
+ 'enhanced_vpc_routing': resource['EnhancedVpcRouting']
+
+ }
+
+ for node in resource['ClusterNodes']:
+ if node['NodeRole'] in ('SHARED', 'LEADER'):
+ facts['private_ip_address'] = node['PrivateIPAddress']
+ if facts['enhanced_vpc_routing'] is False:
+ facts['public_ip_address'] = node['PublicIPAddress']
+ else:
+ facts['public_ip_address'] = None
+ break
+
+ # Some parameters are not ready instantly if you don't wait for available
+ # cluster status
+ facts['create_time'] = None
+ facts['url'] = None
+ facts['port'] = None
+ facts['availability_zone'] = None
+ facts['tags'] = {}
+
+ if resource['ClusterStatus'] != "creating":
+ facts['create_time'] = resource['ClusterCreateTime']
+ facts['url'] = resource['Endpoint']['Address']
+ facts['port'] = resource['Endpoint']['Port']
+ facts['availability_zone'] = resource['AvailabilityZone']
+ facts['tags'] = boto3_tag_list_to_ansible_dict(resource['Tags'])
+
+ return facts
+
+
+@AWSRetry.jittered_backoff()
+def _describe_cluster(redshift, identifier):
+ '''
+ Basic wrapper around describe_clusters with a retry applied
+ '''
+ return redshift.describe_clusters(ClusterIdentifier=identifier)['Clusters'][0]
+
+
+@AWSRetry.jittered_backoff()
+def _create_cluster(redshift, **kwargs):
+ '''
+ Basic wrapper around create_cluster with a retry applied
+ '''
+ return redshift.create_cluster(**kwargs)
+
+
+# Simple wrapper around delete, try to avoid throwing an error if some other
+# operation is in progress
+@AWSRetry.jittered_backoff(catch_extra_error_codes=['InvalidClusterState'])
+def _delete_cluster(redshift, **kwargs):
+ '''
+ Basic wrapper around delete_cluster with a retry applied.
+ Explicitly catches 'InvalidClusterState' (~ Operation in progress) so that
+ we can still delete a cluster if some kind of change operation was in
+ progress.
+ '''
+ return redshift.delete_cluster(**kwargs)
+
+
+@AWSRetry.jittered_backoff(catch_extra_error_codes=['InvalidClusterState'])
+def _modify_cluster(redshift, **kwargs):
+ '''
+ Basic wrapper around modify_cluster with a retry applied.
+ Explicitly catches 'InvalidClusterState' (~ Operation in progress) for cases
+ where another modification is still in progress
+ '''
+ return redshift.modify_cluster(**kwargs)
+
+
+def create_cluster(module, redshift):
+ """
+ Create a new cluster
+
+ module: AnsibleAWSModule object
+ redshift: authenticated redshift connection object
+
+ Returns:
+ """
+
+ identifier = module.params.get('identifier')
+ node_type = module.params.get('node_type')
+ username = module.params.get('username')
+ password = module.params.get('password')
+ d_b_name = module.params.get('db_name')
+ wait = module.params.get('wait')
+ wait_timeout = module.params.get('wait_timeout')
+ tags = module.params.get('tags')
+
+ changed = True
+ # Package up the optional parameters
+ params = {}
+ for p in ('cluster_type', 'cluster_security_groups',
+ 'vpc_security_group_ids', 'cluster_subnet_group_name',
+ 'availability_zone', 'preferred_maintenance_window',
+ 'cluster_parameter_group_name',
+ 'automated_snapshot_retention_period', 'port',
+ 'cluster_version', 'allow_version_upgrade',
+ 'number_of_nodes', 'publicly_accessible', 'encrypted',
+ 'elastic_ip', 'enhanced_vpc_routing'):
+ # https://github.com/boto/boto3/issues/400
+ if module.params.get(p) is not None:
+ params[p] = module.params.get(p)
+
+ if d_b_name:
+ params['d_b_name'] = d_b_name
+ if tags:
+ tags = ansible_dict_to_boto3_tag_list(tags)
+ params['tags'] = tags
+
+ try:
+ _describe_cluster(redshift, identifier)
+ changed = False
+ except is_boto3_error_code('ClusterNotFound'):
+ try:
+ _create_cluster(redshift,
+ ClusterIdentifier=identifier,
+ NodeType=node_type,
+ MasterUsername=username,
+ MasterUserPassword=password,
+ **snake_dict_to_camel_dict(params, capitalize_first=True))
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Failed to create cluster")
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except
+ module.fail_json_aws(e, msg="Failed to describe cluster")
+ if wait:
+ attempts = wait_timeout // 60
+ waiter = redshift.get_waiter('cluster_available')
+ try:
+ waiter.wait(
+ ClusterIdentifier=identifier,
+ WaiterConfig=dict(MaxAttempts=attempts)
+ )
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
+ module.fail_json_aws(e, msg="Timeout waiting for the cluster creation")
+ try:
+ resource = _describe_cluster(redshift, identifier)
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Failed to describe cluster")
+
+ if tags:
+ if _ensure_tags(redshift, identifier, resource['Tags'], module):
+ changed = True
+ resource = _describe_cluster(redshift, identifier)
+
+ return changed, _collect_facts(resource)
+
+
+def describe_cluster(module, redshift):
+ """
+ Collect data about the cluster.
+
+ module: Ansible module object
+ redshift: authenticated redshift connection object
+ """
+ identifier = module.params.get('identifier')
+
+ try:
+ resource = _describe_cluster(redshift, identifier)
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Error describing cluster")
+
+ return True, _collect_facts(resource)
+
+
+def delete_cluster(module, redshift):
+ """
+ Delete a cluster.
+
+ module: Ansible module object
+ redshift: authenticated redshift connection object
+ """
+
+ identifier = module.params.get('identifier')
+ wait = module.params.get('wait')
+ wait_timeout = module.params.get('wait_timeout')
+
+ params = {}
+ for p in ('skip_final_cluster_snapshot',
+ 'final_cluster_snapshot_identifier'):
+ if p in module.params:
+ # https://github.com/boto/boto3/issues/400
+ if module.params.get(p) is not None:
+ params[p] = module.params.get(p)
+
+ try:
+ _delete_cluster(
+ redshift,
+ ClusterIdentifier=identifier,
+ **snake_dict_to_camel_dict(params, capitalize_first=True))
+ except is_boto3_error_code('ClusterNotFound'):
+ return False, {}
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
+ module.fail_json_aws(e, msg="Failed to delete cluster")
+
+ if wait:
+ attempts = wait_timeout // 60
+ waiter = redshift.get_waiter('cluster_deleted')
+ try:
+ waiter.wait(
+ ClusterIdentifier=identifier,
+ WaiterConfig=dict(MaxAttempts=attempts)
+ )
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
+ module.fail_json_aws(e, msg="Timeout deleting the cluster")
+
+ return True, {}
+
+
+def modify_cluster(module, redshift):
+ """
+ Modify an existing cluster.
+
+ module: Ansible module object
+ redshift: authenticated redshift connection object
+ """
+
+ identifier = module.params.get('identifier')
+ wait = module.params.get('wait')
+ wait_timeout = module.params.get('wait_timeout')
+
+ # Package up the optional parameters
+ params = {}
+ for p in ('cluster_type', 'cluster_security_groups',
+ 'vpc_security_group_ids', 'cluster_subnet_group_name',
+ 'availability_zone', 'preferred_maintenance_window',
+ 'cluster_parameter_group_name',
+ 'automated_snapshot_retention_period', 'port', 'cluster_version',
+ 'allow_version_upgrade', 'number_of_nodes', 'new_cluster_identifier'):
+ # https://github.com/boto/boto3/issues/400
+ if module.params.get(p) is not None:
+ params[p] = module.params.get(p)
+
+ # enhanced_vpc_routing parameter change needs an exclusive request
+ if module.params.get('enhanced_vpc_routing') is not None:
+ try:
+ _modify_cluster(
+ redshift,
+ ClusterIdentifier=identifier,
+ EnhancedVpcRouting=module.params.get('enhanced_vpc_routing'))
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Couldn't modify redshift cluster %s " % identifier)
+ if wait:
+ attempts = wait_timeout // 60
+ waiter = redshift.get_waiter('cluster_available')
+ try:
+ waiter.wait(
+ ClusterIdentifier=identifier,
+ WaiterConfig=dict(MaxAttempts=attempts))
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
+ module.fail_json_aws(e,
+ msg="Timeout waiting for cluster enhanced vpc routing modification")
+
+ # change the rest
+ try:
+ _modify_cluster(
+ redshift,
+ ClusterIdentifier=identifier,
+ **snake_dict_to_camel_dict(params, capitalize_first=True))
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Couldn't modify redshift cluster %s " % identifier)
+
+ if module.params.get('new_cluster_identifier'):
+ identifier = module.params.get('new_cluster_identifier')
+
+ if wait:
+ attempts = wait_timeout // 60
+ waiter2 = redshift.get_waiter('cluster_available')
+ try:
+ waiter2.wait(
+ ClusterIdentifier=identifier,
+ WaiterConfig=dict(MaxAttempts=attempts)
+ )
+ except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
+ module.fail_json_aws(e, msg="Timeout waiting for cluster modification")
+ try:
+ resource = _describe_cluster(redshift, identifier)
+ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
+ module.fail_json_aws(e, msg="Couldn't modify redshift cluster %s " % identifier)
+
+ if _ensure_tags(redshift, identifier, resource['Tags'], module):
+ resource = redshift.describe_clusters(ClusterIdentifier=identifier)['Clusters'][0]
+
+ return True, _collect_facts(resource)
+
+
+def main():
+ argument_spec = dict(
+ command=dict(choices=['create', 'facts', 'delete', 'modify'], required=True),
+ identifier=dict(required=True),
+ node_type=dict(choices=['ds1.xlarge', 'ds1.8xlarge', 'ds2.xlarge',
+ 'ds2.8xlarge', 'dc1.large', 'dc2.large',
+ 'dc1.8xlarge', 'dw1.xlarge', 'dw1.8xlarge',
+ 'dw2.large', 'dw2.8xlarge'], required=False),
+ username=dict(required=False),
+ password=dict(no_log=True, required=False),
+ db_name=dict(required=False),
+ cluster_type=dict(choices=['multi-node', 'single-node'], default='single-node'),
+ cluster_security_groups=dict(aliases=['security_groups'], type='list', elements='str'),
+ vpc_security_group_ids=dict(aliases=['vpc_security_groups'], type='list', elements='str'),
+ skip_final_cluster_snapshot=dict(aliases=['skip_final_snapshot'],
+ type='bool', default=False),
+ final_cluster_snapshot_identifier=dict(aliases=['final_snapshot_id'], required=False),
+ cluster_subnet_group_name=dict(aliases=['subnet']),
+ availability_zone=dict(aliases=['aws_zone', 'zone']),
+ preferred_maintenance_window=dict(aliases=['maintance_window', 'maint_window']),
+ cluster_parameter_group_name=dict(aliases=['param_group_name']),
+ automated_snapshot_retention_period=dict(aliases=['retention_period'], type='int'),
+ port=dict(type='int'),
+ cluster_version=dict(aliases=['version'], choices=['1.0']),
+ allow_version_upgrade=dict(aliases=['version_upgrade'], type='bool', default=True),
+ number_of_nodes=dict(type='int'),
+ publicly_accessible=dict(type='bool', default=False),
+ encrypted=dict(type='bool', default=False),
+ elastic_ip=dict(required=False),
+ new_cluster_identifier=dict(aliases=['new_identifier']),
+ enhanced_vpc_routing=dict(type='bool', default=False),
+ wait=dict(type='bool', default=False),
+ wait_timeout=dict(type='int', default=300),
+ tags=dict(type='dict', aliases=['resource_tags']),
+ purge_tags=dict(type='bool', default=True)
+ )
+
+ required_if = [
+ ('command', 'delete', ['skip_final_cluster_snapshot']),
+ ('command', 'create', ['node_type',
+ 'username',
+ 'password'])
+ ]
+
+ module = AnsibleAWSModule(
+ argument_spec=argument_spec,
+ required_if=required_if
+ )
+
+ command = module.params.get('command')
+ skip_final_cluster_snapshot = module.params.get('skip_final_cluster_snapshot')
+ final_cluster_snapshot_identifier = module.params.get('final_cluster_snapshot_identifier')
+ # can't use module basic required_if check for this case
+ if command == 'delete' and skip_final_cluster_snapshot is False and final_cluster_snapshot_identifier is None:
+ module.fail_json(msg="Need to specify final_cluster_snapshot_identifier if skip_final_cluster_snapshot is False")
+
+ conn = module.client('redshift')
+
+ changed = True
+ if command == 'create':
+ (changed, cluster) = create_cluster(module, conn)
+
+ elif command == 'facts':
+ (changed, cluster) = describe_cluster(module, conn)
+
+ elif command == 'delete':
+ (changed, cluster) = delete_cluster(module, conn)
+
+ elif command == 'modify':
+ (changed, cluster) = modify_cluster(module, conn)
+
+ module.exit_json(changed=changed, cluster=cluster)
+
+
+if __name__ == '__main__':
+ main()