diff --git a/library/cloud/rds_param_group b/library/cloud/rds_param_group
new file mode 100644
index 0000000000..83f11e4c61
--- /dev/null
+++ b/library/cloud/rds_param_group
@@ -0,0 +1,299 @@
+#!/usr/bin/python
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+
+DOCUMENTATION = '''
+---
+module: rds_param_group
+version_added: "1.5"
+short_description: manage RDS parameter groups
+description:
+ - Creates, modifies, and deletes RDS parameter groups. This module has a dependency on python-boto >= 2.5.
+options:
+ state:
+ description:
+ - Specifies whether the group should be present or absent.
+ required: true
+ default: present
+ aliases: []
+ choices: [ 'present' , 'absent' ]
+ name:
+ description:
+ - Database parameter group identifier.
+ required: true
+ default: null
+ aliases: []
+ description:
+ description:
+ - Database parameter group description. Only set when a new group is added.
+ required: false
+ default: null
+ aliases: []
+ engine:
+ description:
+ - The type of database for this group. Required for state=present.
+ required: false
+ default: null
+ aliases: []
+ choices: [ 'mysql5.1', 'mysql5.5', 'mysql5.6', 'oracle-ee-11.2', 'oracle-se-11.2', 'oracle-se1-11.2', 'postgres9.3', 'sqlserver-ee-10.5', 'sqlserver-ee-11.0', 'sqlserver-ex-10.5', 'sqlserver-ex-11.0', 'sqlserver-se-10.5', 'sqlserver-se-11.0', 'sqlserver-web-10.5', 'sqlserver-web-11.0']
+ immediate:
+ description:
+ - Whether to apply the changes immediately, or after the next reboot of any associated instances.
+ required: false
+ default: null
+ aliases: []
+ params:
+ description:
+ - Map of parameter names and values. Numeric values may be represented as K for kilo (1024), M for mega (1024^2), G for giga (1024^3), or T for tera (1024^4), and these values will be expanded into the appropriate number before being set in the parameter group.
+ required: false
+ default: null
+ aliases: []
+ choices: [ 'mysql5.1', 'mysql5.5', 'mysql5.6', 'oracle-ee-11.2', 'oracle-se-11.2', 'oracle-se1-11.2', 'postgres9.3', 'sqlserver-ee-10.5', 'sqlserver-ee-11.0', 'sqlserver-ex-10.5', 'sqlserver-ex-11.0', 'sqlserver-se-10.5', 'sqlserver-se-11.0', 'sqlserver-web-10.5', 'sqlserver-web-11.0']
+ region:
+ description:
+ - The AWS region to use. If not specified then the value of the EC2_REGION environment variable, if any, is used.
+ required: true
+ default: null
+ aliases: [ 'aws_region', 'ec2_region' ]
+ aws_access_key:
+ description:
+ - AWS access key. If not set then the value of the AWS_ACCESS_KEY environment variable is used.
+ required: false
+ default: null
+ aliases: [ 'ec2_access_key', 'access_key' ]
+ aws_secret_key:
+ description:
+ - AWS secret key. If not set then the value of the AWS_SECRET_KEY environment variable is used.
+ required: false
+ default: null
+ aliases: [ 'ec2_secret_key', 'secret_key' ]
+requirements: [ "boto" ]
+author: Scott Anderson
+'''
+
+EXAMPLES = '''
+# Add or change a parameter group, in this case setting auto_increment_increment to 42 * 1024
+- rds_param_group: >
+ state=present
+ name=norwegian_blue
+ description=My Fancy Ex Parrot Group
+ engine=mysql5.6
+ params='{"auto_increment_increment": "42K"}'
+
+# Remove a parameter group
+- rds_param_group: >
+ state=absent
+ name=norwegian_blue
+'''
+
+import sys
+import time
+
+VALID_ENGINES = [
+ 'mysql5.1',
+ 'mysql5.5',
+ 'mysql5.6',
+ 'oracle-ee-11.2',
+ 'oracle-se-11.2',
+ 'oracle-se1-11.2',
+ 'postgres9.3',
+ 'sqlserver-ee-10.5',
+ 'sqlserver-ee-11.0',
+ 'sqlserver-ex-10.5',
+ 'sqlserver-ex-11.0',
+ 'sqlserver-se-10.5',
+ 'sqlserver-se-11.0',
+ 'sqlserver-web-10.5',
+ 'sqlserver-web-11.0',
+]
+
+try:
+ import boto.rds
+ from boto.exception import BotoServerError
+except ImportError:
+ print "failed=True msg='boto required for this module'"
+ sys.exit(1)
+
+# returns a tuple: (whether or not a parameter was changed, the remaining parameters that weren't found in this parameter group)
+
+class NotModifiableError(StandardError):
+ def __init__(self, error_message, *args):
+ super(NotModifiableError, self).__init__(error_message, *args)
+ self.error_message = error_message
+
+ def __repr__(self):
+ return 'NotModifiableError: %s' % self.error_message
+
+ def __str__(self):
+ return 'NotModifiableError: %s' % self.error_message
+
+INT_MODIFIERS = {
+ 'K': 1024,
+ 'M': pow(1024, 2),
+ 'G': pow(1024, 3),
+ 'T': pow(1024, 4),
+}
+
+TRUE_VALUES = ('on', 'true', 'yes', '1',)
+
+def set_parameter(param, value, immediate):
+ """
+ Allows setting parameters with 10M = 10* 1024 * 1024 and so on.
+ """
+ converted_value = value
+
+ if param.type == 'string':
+ converted_value = str(value)
+
+ elif param.type == 'integer':
+ if isinstance(value, basestring):
+ for modifier in INT_MODIFIERS.keys():
+ if value.endswith(modifier):
+ converted_value = int(value[:-1]) * INT_MODIFIERS[modifier]
+ converted_value = int(converted_value)
+ elif type(value) == bool:
+ converted_value = 1 if value else 0
+ else:
+ converted_value = int(value)
+
+ elif param.type == 'boolean':
+ if isinstance(value, basestring):
+ converted_value = value in TRUE_VALUES
+ else:
+ converted_value = bool(value)
+
+ param.value = converted_value
+ param.apply(immediate)
+
+def modify_group(group, params, immediate=False):
+ """ Set all of the params in a group to the provided new params. Raises NotModifiableError if any of the
+ params to be changed are read only.
+ """
+ changed = {}
+
+ new_params = dict(params)
+
+ for key in new_params.keys():
+ if group.has_key(key):
+ param = group[key]
+ new_value = new_params[key]
+
+ if param.value != new_value:
+ if not param.is_modifiable:
+ raise NotModifiableError('Parameter %s is not modifiable.' % key)
+
+ changed[key] = {'old': param.value, 'new': new_value}
+
+ set_parameter(param, new_value, immediate)
+
+ del new_params[key]
+
+ return changed, new_params
+
+
+def main():
+ argument_spec = ec2_argument_spec()
+ argument_spec.update(dict(
+ state = dict(required=True, choices=['present', 'absent']),
+ name = dict(required=True),
+ engine = dict(required=False, choices=VALID_ENGINES),
+ description = dict(required=False),
+ params = dict(required=False, aliases=['parameters'], type='dict'),
+ immediate = dict(required=False, type='bool'),
+ )
+ )
+ module = AnsibleModule(argument_spec=argument_spec)
+
+ state = module.params.get('state')
+ group_name = module.params.get('name').lower()
+ group_engine = module.params.get('engine')
+ group_description = module.params.get('description')
+ group_params = module.params.get('params') or {}
+ immediate = module.params.get('immediate') or False
+
+ if state == 'present':
+ for required in ['name', 'description', 'engine', 'params']:
+ if not module.params.get(required):
+ module.fail_json(msg = str("Parameter %s required for state='present'" % required))
+ else:
+ for not_allowed in ['description', 'engine', 'params']:
+ if module.params.get(not_allowed):
+ module.fail_json(msg = str("Parameter %s not allowed for state='absent'" % not_allowed))
+
+ # Retrieve any AWS settings from the environment.
+ ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
+
+ if not region:
+ module.fail_json(msg = str("region not specified and unable to determine region from EC2_REGION."))
+
+ try:
+ conn = boto.rds.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
+ except boto.exception.BotoServerError, e:
+ module.fail_json(msg = e.error_message)
+
+ group_was_added = False
+
+ try:
+ changed = False
+
+ try:
+ all_groups = conn.get_all_dbparameter_groups(group_name, max_records=100)
+ exists = len(all_groups) > 0
+ except BotoServerError, e:
+ if e.error_code != 'DBParameterGroupNotFound':
+ module.fail_json(msg = e.error_message)
+ exists = False
+
+ if state == 'absent':
+ if exists:
+ conn.delete_parameter_group(group_name)
+ changed = True
+ else:
+ changed = {}
+ if not exists:
+ new_group = conn.create_parameter_group(group_name, engine=group_engine, description=group_description)
+ group_was_added = True
+
+ # If a "Marker" is present, this group has more attributes remaining to check. Get the next batch, but only
+ # if there are parameters left to set.
+ marker = None
+ while len(group_params):
+ next_group = conn.get_all_dbparameters(group_name, marker=marker)
+
+ changed_params, group_params = modify_group(next_group, group_params, immediate)
+ changed.update(changed_params)
+
+ if hasattr(next_group, 'Marker'):
+ marker = next_group.Marker
+ else:
+ break
+
+
+ except BotoServerError, e:
+ module.fail_json(msg = e.error_message)
+
+ except NotModifiableError, e:
+ msg = e.error_message
+ if group_was_added:
+ msg = '%s The group "%s" was added first.' % (msg, group_name)
+ module.fail_json(msg=msg)
+
+ module.exit_json(changed=changed)
+
+# import module snippets
+from ansible.module_utils.basic import *
+from ansible.module_utils.ec2 import *
+
+main()
diff --git a/library/cloud/rds_subnet_group b/library/cloud/rds_subnet_group
new file mode 100644
index 0000000000..1688856719
--- /dev/null
+++ b/library/cloud/rds_subnet_group
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see .
+
+DOCUMENTATION = '''
+---
+module: rds_subnet_group
+version_added: "1.5"
+short_description: manage RDS database subnet groups
+description:
+ - Creates, modifies, and deletes RDS database subnet groups. This module has a dependency on python-boto >= 2.5.
+options:
+ state:
+ description:
+ - Specifies whether the subnet should be present or absent.
+ required: true
+ default: present
+ aliases: []
+ choices: [ 'present' , 'absent' ]
+ name:
+ description:
+ - Database subnet group identifier.
+ required: true
+ default: null
+ aliases: []
+ description:
+ description:
+ - Database subnet group description. Only set when a new group is added.
+ required: false
+ default: null
+ aliases: []
+ subnets:
+ description:
+ - List of subnet IDs that make up the database subnet group.
+ required: false
+ default: null
+ aliases: []
+ region:
+ description:
+ - The AWS region to use. If not specified then the value of the EC2_REGION environment variable, if any, is used.
+ required: true
+ default: null
+ aliases: [ 'aws_region', 'ec2_region' ]
+ aws_access_key:
+ description:
+ - AWS access key. If not set then the value of the AWS_ACCESS_KEY environment variable is used.
+ required: false
+ default: null
+ aliases: [ 'ec2_access_key', 'access_key' ]
+ aws_secret_key:
+ description:
+ - AWS secret key. If not set then the value of the AWS_SECRET_KEY environment variable is used.
+ required: false
+ default: null
+ aliases: [ 'ec2_secret_key', 'secret_key' ]
+requirements: [ "boto" ]
+author: Scott Anderson
+'''
+
+EXAMPLES = '''
+# Add or change a subnet group
+- local_action:
+ module: rds_subnet_group
+ state: present
+ name: norwegian-blue
+ description: My Fancy Ex Parrot Subnet Group
+ subnets:
+ - subnet-aaaaaaaa
+ - subnet-bbbbbbbb
+
+# Remove a parameter group
+- rds_param_group: >
+ state=absent
+ name=norwegian-blue
+'''
+
+import sys
+import time
+
+try:
+ import boto.rds
+ from boto.exception import BotoServerError
+except ImportError:
+ print "failed=True msg='boto required for this module'"
+ sys.exit(1)
+
+def main():
+ argument_spec = ec2_argument_spec()
+ argument_spec.update(dict(
+ state = dict(required=True, choices=['present', 'absent']),
+ name = dict(required=True),
+ description = dict(required=False),
+ subnets = dict(required=False, type='list'),
+ )
+ )
+ module = AnsibleModule(argument_spec=argument_spec)
+
+ state = module.params.get('state')
+ group_name = module.params.get('name').lower()
+ group_description = module.params.get('description')
+ group_subnets = module.params.get('subnets') or {}
+
+ if state == 'present':
+ for required in ['name', 'description', 'subnets']:
+ if not module.params.get(required):
+ module.fail_json(msg = str("Parameter %s required for state='present'" % required))
+ else:
+ for not_allowed in ['description', 'subnets']:
+ if module.params.get(not_allowed):
+ module.fail_json(msg = str("Parameter %s not allowed for state='absent'" % not_allowed))
+
+ # Retrieve any AWS settings from the environment.
+ ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
+
+ if not region:
+ module.fail_json(msg = str("region not specified and unable to determine region from EC2_REGION."))
+
+ try:
+ conn = boto.rds.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
+ except boto.exception.BotoServerError, e:
+ module.fail_json(msg = e.error_message)
+
+ try:
+ changed = False
+ exists = False
+
+ try:
+ matching_groups = conn.get_all_db_subnet_groups(group_name, max_records=100)
+ exists = len(matching_groups) > 0
+ except BotoServerError, e:
+ if e.error_code != 'DBSubnetGroupNotFoundFault':
+ module.fail_json(msg = e.error_message)
+
+ if state == 'absent':
+ if exists:
+ conn.delete_db_subnet_group(group_name)
+ changed = True
+ else:
+ if not exists:
+ new_group = conn.create_db_subnet_group(group_name, desc=group_description, subnet_ids=group_subnets)
+
+ else:
+ changed_group = conn.modify_db_subnet_group(group_name, description=group_description, subnet_ids=group_subnets)
+
+ except BotoServerError, e:
+ module.fail_json(msg = e.error_message)
+
+ module.exit_json(changed=changed)
+
+# import module snippets
+from ansible.module_utils.basic import *
+from ansible.module_utils.ec2 import *
+
+main()