diff --git a/cloud/ec2_asg b/cloud/ec2_asg
index 15df0ed41b9..0e43e6b2524 100644
--- a/cloud/ec2_asg
+++ b/cloud/ec2_asg
@@ -37,23 +37,27 @@ options:
   load_balancers:
     description:
       - List of ELB names to use for the group
-    required: true
+    required: false
   availability_zones:
     description:
       - List of availability zone names in which to create the group.
-    required: true
+    required: false
   launch_config_name:
     description:
       - Name of the Launch configuration to use for the group. See the ec2_lc module for managing these.
-    required: true
+    required: false
   min_size:
     description:
       - Minimum number of instances in group
-    required: true
+    required: false
   max_size:
     description:
       - Maximum number of instances in group
-    required: true
+    required: false
+  desired_capacity:
+    description:
+      - Desired number of instances in group
+    required: false
   aws_secret_key:
     description:
       - AWS secret key. If not set then the value of the AWS_SECRET_KEY environment variable is used.
@@ -71,16 +75,23 @@ options:
       - The AWS region to use. If not specified then the value of the EC2_REGION environment variable, if any, is used.
     required: false
     aliases: ['aws_region', 'ec2_region']
+  vpc_zone_identifier:
+    description:
+      - List of VPC subnets to use
+    required: false
+    default: None
 """
 
 EXAMPLES = '''
-- ec2_asg: >
+- ec2_asg:
     name: special
     load_balancers: 'lb1,lb2'
     availability_zones: 'eu-west-1a,eu-west-1b'
     launch_config_name: 'lc-1'
     min_size: 1
     max_size: 10
+    desired_capacity: 5
+    vpc_zone_identifier: 'subnet-abcd1234,subnet-1a2b3c4d'
 '''
 
 import sys
@@ -97,40 +108,72 @@ except ImportError:
     print "failed=True msg='boto required for this module'"
     sys.exit(1)
 
-AWS_REGIONS = ['ap-northeast-1',
-               'ap-southeast-1',
-               'ap-southeast-2',
-               'eu-west-1',
-               'sa-east-1',
-               'us-east-1',
-               'us-west-1',
-               'us-west-2']
+
+def enforce_required_arguments(module):
+    ''' As many arguments are not required for autoscale group deletion
+        they cannot be mandatory arguments for the module, so we enforce
+        them here '''
+    missing_args = []
+    for arg in ('min_size', 'max_size', 'launch_config_name', 'availability_zones'):
+        if module.params[arg] is None:
+            missing_args.append(arg)
+    if missing_args:
+        module.fail_json(msg="Missing required arguments for autoscaling group create/update: %s" % ",".join(missing_args))
 
 
 def create_autoscaling_group(connection, module):
+    enforce_required_arguments(module)
+
     group_name = module.params.get('name')
     load_balancers = module.params['load_balancers']
     availability_zones = module.params['availability_zones']
     launch_config_name = module.params.get('launch_config_name')
-    min_size = module.params.get('min_size')
-    max_size = module.params.get('max_size')
+    min_size = module.params['min_size']
+    max_size = module.params['max_size']
+    desired_capacity = module.params.get('desired_capacity')
+    vpc_zone_identifier = module.params.get('vpc_zone_identifier')
 
-    launch_configs = connection.get_all_launch_configurations(name=[launch_config_name])
+    launch_configs = connection.get_all_launch_configurations(names=[launch_config_name])
 
-    ag = AutoScalingGroup(
-        group_name=group_name,
-        load_balancers=load_balancers,
-        availability_zones=availability_zones,
-        launch_config=launch_configs[0],
-        min_size=min_size,
-        max_size=max_size,
-        connection=connection)
+    as_groups = connection.get_all_groups(names=[group_name])
 
-    try:
-        connection.create_auto_scaling_group(ag)
-        module.exit_json(changed=True)
-    except BotoServerError, e:
-        module.exit_json(changed=False, msg=str(e))
+    if not as_groups:
+        ag = AutoScalingGroup(
+                 group_name=group_name,
+                 load_balancers=load_balancers,
+                 availability_zones=availability_zones,
+                 launch_config=launch_configs[0],
+                 min_size=min_size,
+                 max_size=max_size,
+                 desired_capacity=desired_capacity,
+                 vpc_zone_identifier=vpc_zone_identifier,
+                 connection=connection)
+
+        try:
+            connection.create_auto_scaling_group(ag)
+            module.exit_json(changed=True)
+        except BotoServerError, e:
+            module.fail_json(msg=str(e))
+    else:
+        as_group = as_groups[0]
+        changed = False
+        for attr in ('launch_config_name', 'max_size', 'min_size', 'desired_capacity',
+                     'vpc_zone_identifier', 'availability_zones'):
+            if getattr(as_group, attr) != module.params.get(attr):
+                changed = True
+                setattr(as_group, attr, module.params.get(attr))
+        # handle loadbalancers separately because None != []
+        load_balancers = module.params.get('load_balancers') or []
+        if as_group.load_balancers != load_balancers:
+            changed = True
+            as_group.load_balancers = module.params.get('load_balancers')
+
+        try:
+            if changed:
+                as_group.update()
+            module.exit_json(changed=changed)
+        except BotoServerError, e:
+            module.fail_json(msg=str(e))
 
 
 def delete_autoscaling_group(connection, module):
@@ -156,35 +199,33 @@ def delete_autoscaling_group(connection, module):
 
 
 def main():
-    module = AnsibleModule(
-        argument_spec = dict(
-            name = dict(required=True, type='str'),
-            load_balancers = dict(required=False, type='list'),
-            availability_zones = dict(required=True, type='list'),
-            launch_config_name = dict(required=True, type='str'),
-            min_size = dict(required=True, type='int'),
-            max_size = dict(required=True, type='int'),
-            state = dict(default='present', choices=['present', 'absent']),
-            region = dict(aliases=['aws_region', 'ec2_region'], choices=AWS_REGIONS),
-            ec2_url = dict(),
-            ec2_secret_key = dict(aliases=['aws_secret_key', 'secret_key'], no_log=True),
-            ec2_access_key = dict(aliases=['aws_access_key', 'access_key']),
+    argument_spec = ec2_argument_spec()
+    argument_spec.update(
+        dict(
+            name=dict(required=True, type='str'),
+            load_balancers=dict(type='list'),
+            availability_zones=dict(type='list'),
+            launch_config_name=dict(type='str'),
+            min_size=dict(type='int'),
+            max_size=dict(type='int'),
+            desired_capacity=dict(type='int'),
+            vpc_zone_identifier=dict(type='str'),
+            state=dict(default='present', choices=['present', 'absent']),
         )
     )
-
-    ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
+    module = AnsibleModule(argument_spec=argument_spec)
 
     state = module.params.get('state')
 
+    region, ec2_url, aws_connect_params = get_aws_connection_info(module)
     try:
-        connection = boto.ec2.autoscale.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
+        connection = connect_to_aws(boto.ec2.autoscale, region, **aws_connect_params)
     except boto.exception.NoAuthHandlerFound, e:
-        module.fail_json(msg = str(e))
+        module.fail_json(msg=str(e))
 
     if state == 'present':
         create_autoscaling_group(connection, module)
     elif state == 'absent':
         delete_autoscaling_group(connection, module)
 
-
 main()
diff --git a/cloud/ec2_lc b/cloud/ec2_lc
index 0f2dc26a234..ff24924aaa6 100644
--- a/cloud/ec2_lc
+++ b/cloud/ec2_lc
@@ -37,15 +37,15 @@ options:
   image_id:
     description:
       - The AMI unique identifier to be used for the group
-    required: true
+    required: false
   key_name:
     description:
       - The SSH key name to be used for access to managed instances
-    required: true
+    required: false
   security_groups:
     description:
       - A list of security groups into which instances should be found
-    required: true
+    required: false
   aws_secret_key:
     description:
       - AWS secret key. If not set then the value of the AWS_SECRET_KEY environment variable is used.
@@ -63,6 +63,18 @@ options:
       - The AWS region to use. If not specified then the value of the EC2_REGION environment variable, if any, is used.
     required: false
     aliases: ['aws_region', 'ec2_region']
+  volumes:
+    description:
+      - a list of volume dicts, each containing device name and optionally ephemeral id or snapshot id. Size and type (and number of iops for io device type) must be specified for a new volume or a root volume, and may be passed for a snapshot volume. For any volume, a volume size less than 1 will be interpreted as a request not to create the volume.
+    required: false
+    default: null
+    aliases: []
+  user_data:
+    description:
+      - opaque blob of data which is made available to the ec2 instance
+    required: false
+    default: null
+    aliases: []
 """
 
 EXAMPLES = '''
@@ -81,6 +93,7 @@ from ansible.module_utils.basic import *
 from ansible.module_utils.ec2 import *
 
 try:
+    from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
     import boto.ec2.autoscale
     from boto.ec2.autoscale import LaunchConfiguration
     from boto.exception import BotoServerError
@@ -88,14 +101,26 @@ except ImportError:
     print "failed=True msg='boto required for this module'"
     sys.exit(1)
 
-AWS_REGIONS = ['ap-northeast-1',
-               'ap-southeast-1',
-               'ap-southeast-2',
-               'eu-west-1',
-               'sa-east-1',
-               'us-east-1',
-               'us-west-1',
-               'us-west-2']
+
+def create_block_device(module, volume):
+    # Not aware of a way to determine this programatically
+    # http://aws.amazon.com/about-aws/whats-new/2013/10/09/ebs-provisioned-iops-maximum-iops-gb-ratio-increased-to-30-1/
+    MAX_IOPS_TO_SIZE_RATIO = 30
+    if 'snapshot' not in volume and 'ephemeral' not in volume:
+        if 'volume_size' not in volume:
+            module.fail_json(msg='Size must be specified when creating a new volume or modifying the root volume')
+    if 'snapshot' in volume:
+        if 'device_type' in volume and volume.get('device_type') == 'io1' and 'iops' not in volume:
+            module.fail_json(msg='io1 volumes must have an iops value set')
+    if 'ephemeral' in volume:
+        if 'snapshot' in volume:
+            module.fail_json(msg='Cannot set both ephemeral and snapshot')
+    return BlockDeviceType(snapshot_id=volume.get('snapshot'),
+                           ephemeral_name=volume.get('ephemeral'),
+                           size=volume.get('volume_size'),
+                           volume_type=volume.get('device_type'),
+                           delete_on_termination=volume.get('delete_on_termination', False),
+                           iops=volume.get('iops'))
 
 
 def create_launch_config(connection, module):
@@ -103,23 +128,48 @@ def create_launch_config(connection, module):
     image_id = module.params.get('image_id')
     key_name = module.params.get('key_name')
     security_groups = module.params['security_groups']
+    user_data = module.params.get('user_data')
+    volumes = module.params['volumes']
+    instance_type = module.params.get('instance_type')
+    bdm = BlockDeviceMapping()
+
+    if volumes:
+        for volume in volumes:
+            if 'device_name' not in volume:
+                module.fail_json(msg='Device name must be set for volume')
+            # Minimum volume size is 1GB. We'll use volume size explicitly set to 0
+            # to be a signal not to create this volume
+            if 'volume_size' not in volume or int(volume['volume_size']) > 0:
+                bdm[volume['device_name']] = create_block_device(module, volume)
 
     lc = LaunchConfiguration(
         name=name,
         image_id=image_id,
         key_name=key_name,
-        security_groups=security_groups)
+        security_groups=security_groups,
+        user_data=user_data,
+        block_device_mappings=[bdm],
+        instance_type=instance_type)
 
-    try:
-        connection.create_launch_configuration(lc)
-        module.exit_json(changed=True)
-    except BotoServerError, e:
-        module.exit_json(changed=False, msg=str(e))
+    launch_configs = connection.get_all_launch_configurations(names=[name])
+    changed = False
+    if not launch_configs:
+        try:
+            connection.create_launch_configuration(lc)
+            launch_configs = connection.get_all_launch_configurations(names=[name])
+            changed = True
+        except BotoServerError, e:
+            module.fail_json(msg=str(e))
+    result = launch_configs[0]
+
+    module.exit_json(changed=changed, name=result.name, created_time=str(result.created_time),
+                     image_id=result.image_id, arn=result.launch_configuration_arn,
+                     security_groups=result.security_groups, instance_type=instance_type)
 
 
 def delete_launch_config(connection, module):
     name = module.params.get('name')
-    launch_configs = connection.get_all_launch_configurations(name=[name])
+    launch_configs = connection.get_all_launch_configurations(names=[name])
     if launch_configs:
         launch_configs[0].delete()
         module.exit_json(changed=True)
@@ -128,26 +178,28 @@ def delete_launch_config(connection, module):
 
 
 def main():
-    module = AnsibleModule(
-        argument_spec = dict(
-            name = dict(required=True, type='str'),
-            image_id = dict(required=True, type='str'),
-            key_name = dict(required=True, type='str'),
-            security_groups = dict(required=True, type='list'),
-            state = dict(default='present', choices=['present', 'absent']),
-            region = dict(aliases=['aws_region', 'ec2_region'], choices=AWS_REGIONS),
-            ec2_url = dict(),
-            ec2_secret_key = dict(aliases=['aws_secret_key', 'secret_key'], no_log=True),
-            ec2_access_key = dict(aliases=['aws_access_key', 'access_key']),
+    argument_spec = ec2_argument_spec()
+    argument_spec.update(
+        dict(
+            name=dict(required=True, type='str'),
+            image_id=dict(type='str'),
+            key_name=dict(type='str'),
+            security_groups=dict(type='list'),
+            user_data=dict(type='str'),
+            volumes=dict(type='list'),
+            instance_type=dict(type='str'),
+            state=dict(default='present', choices=['present', 'absent']),
         )
     )
 
-    ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
+    module = AnsibleModule(argument_spec=argument_spec)
+
+    region, ec2_url, aws_connect_params = get_aws_connection_info(module)
 
     try:
-        connection = boto.ec2.autoscale.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
+        connection = connect_to_aws(boto.ec2.autoscale, region, **aws_connect_params)
     except boto.exception.NoAuthHandlerFound, e:
-        module.fail_json(msg = str(e))
+        module.fail_json(msg=str(e))
 
     state = module.params.get('state')