diff --git a/.travis.yml b/.travis.yml
index aa013eb5e0c9..f706fb66a281 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@ python:
   - "2.7"
   - "3.3"
 install:
-  - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
+  - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2==0.5.1; fi
   - pip install -r requirements.txt
   - python setup.py develop
   - pip install coverage python-coveralls
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 9a64aaec78e5..910f89f07f54 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -2,6 +2,18 @@
 CHANGELOG
 =========
 
+Next Release (TBD)
+==================
+
+* feature:``--generate-cli-skeleton``: Generates a JSON skeleton to fill out
+  and be used as input to ``--cli-input-json``.
+  (`issue 963 <https://github.com/aws/aws-cli/pull/963>`_)
+* feature:``--cli-input-json``: Runs an operation using a global JSON file
+  that supplies all of the operation's arguments. This JSON file can
+  be generated by ``--generate-cli-skeleton``.
+  (`issue 963 <https://github.com/aws/aws-cli/pull/963>`_)
+
+
 1.5.4
 =====
 
diff --git a/awscli/clidriver.py b/awscli/clidriver.py
index 0dde5bde6b5c..48bdaff99e0f 100644
--- a/awscli/clidriver.py
+++ b/awscli/clidriver.py
@@ -440,6 +440,9 @@ def arg_table(self):
     def __call__(self, args, parsed_globals):
         # Once we know we're trying to call a particular operation
         # of a service we can go ahead and load the parameters.
+        event = 'before-building-argument-table-parser.%s.%s' % \
+            (self._parent_name, self._name)
+        self._emit(event, argument_table=self.arg_table, args=args)
         operation_parser = self._create_operation_parser(self.arg_table)
         self._add_help(operation_parser)
         parsed_args, remaining = operation_parser.parse_known_args(args)
@@ -457,8 +460,32 @@ def __call__(self, args, parsed_globals):
                    parsed_globals=parsed_globals)
         call_parameters = self._build_call_parameters(parsed_args,
                                                       self.arg_table)
-        return self._operation_caller.invoke(
-            self._operation_object, call_parameters, parsed_globals)
+        event = 'calling-command.%s.%s' % (self._parent_name,
+                                           self._name)
+        override = self._emit_first_non_none_response(
+            event,
+            call_parameters=call_parameters,
+            parsed_args=parsed_args,
+            parsed_globals=parsed_globals
+        )
+        # There are two possible values for override. It can be some type
+        # of exception that will be raised if detected or it can represent
+        # the desired return code. Note that a return code of 0 represents
+        # a success.
+        if override is not None:
+            if isinstance(override, Exception):
+                # If the override value provided back is an exception then
+                # raise the exception
+                raise override
+            else:
+                # This is the value usually returned by the ``invoke()``
+                # method of the operation caller. It represents the return
+                # code of the operation.
+                return override
+        else:
+            # No override value was supplied.
+            return self._operation_caller.invoke(
+                self._operation_object, call_parameters, parsed_globals)
 
     def create_help_command(self):
         return OperationHelpCommand(
@@ -525,6 +552,10 @@ def _emit(self, name, **kwargs):
         session = self._service_object.session
         return session.emit(name, **kwargs)
 
+    def _emit_first_non_none_response(self, name, **kwargs):
+        session = self._service_object.session
+        return session.emit_first_non_none_response(name, **kwargs)
+
     def _create_operation_parser(self, arg_table):
         parser = ArgTableArgParser(arg_table)
         return parser
diff --git a/awscli/customizations/arguments.py b/awscli/customizations/arguments.py
new file mode 100644
index 000000000000..ee47dbe778c2
--- /dev/null
+++ b/awscli/customizations/arguments.py
@@ -0,0 +1,53 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+from awscli.arguments import CustomArgument
+
+
+class OverrideRequiredArgsArgument(CustomArgument):
+    """An argument that if specified makes all other arguments not required
+
+    By not required, it refers to not having an error thrown when the
+    parser does not find an argument that is required on the command line.
+    To obtain this argument's property of ignoring required arguments,
+    subclass from this class and fill out the ``ARG_DATA`` parameter as
+    described below. Note this class is really only useful for subclassing.
+    """
+
+    # ``ARG_DATA`` follows the same format as a member of ``ARG_TABLE`` in
+    # ``BasicCommand`` class as specified in
+    # ``awscli/customizations/commands.py``.
+    #
+    # For example, an ``ARG_DATA`` variable would be filled out as:
+    #
+    # ARG_DATA =
+    # {'name': 'my-argument',
+    #  'help_text': 'This is argument ensures the argument is specified'
+    #               'no other arguments are required'}
+    ARG_DATA = {'name': 'no-required-args'}
+
+    def __init__(self, session):
+        self._session = session
+        self._register_argument_action()
+        super(OverrideRequiredArgsArgument, self).__init__(**self.ARG_DATA)
+
+    def _register_argument_action(self):
+        self._session.register('before-building-argument-table-parser',
+                               self.override_required_args)
+
+    def override_required_args(self, argument_table, args, **kwargs):
+        name_in_cmdline = '--' + self.name
+        # Set all ``Argument`` objects in ``argument_table`` to not required
+        # if this argument's name is present in the command line.
+        if name_in_cmdline in args:
+            for arg_name in argument_table.keys():
+                argument_table[arg_name].required = False
diff --git a/awscli/customizations/cliinputjson.py b/awscli/customizations/cliinputjson.py
new file mode 100644
index 000000000000..e986840eec44
--- /dev/null
+++ b/awscli/customizations/cliinputjson.py
@@ -0,0 +1,85 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import json
+
+from awscli.paramfile import get_paramfile
+from awscli.argprocess import ParamError
+from awscli.customizations.arguments import OverrideRequiredArgsArgument
+
+
+def register_cli_input_json(cli):
+    cli.register('building-argument-table', add_cli_input_json)
+
+
+def add_cli_input_json(operation, argument_table, **kwargs):
+    # This argument cannot support operations with streaming output which
+    # is designated by the argument name `outfile`.
+    if 'outfile' not in argument_table:
+        cli_input_json_argument = CliInputJSONArgument(operation)
+        cli_input_json_argument.add_to_arg_table(argument_table)
+
+
+class CliInputJSONArgument(OverrideRequiredArgsArgument):
+    """This argument inputs a JSON string as the entire input for a command.
+
+    Ideally, the value to this argument should be a filled out JSON file
+    generated by ``--generate-cli-skeleton``. The items in the JSON string
+    will not clobber other arguments entered into the command line.
+    """
+    ARG_DATA = {
+        'name': 'cli-input-json',
+        'help_text': 'Performs service operation based on the JSON string '
+                     'provided. The JSON string follows the format provided '
+                     'by ``--generate-cli-skeleton``. If other arguments are '
+                     'provided on the command line, it will not clobber their '
+                     'values.'
+    }
+
+    def __init__(self, operation_object):
+        self._operation_object = operation_object
+        super(CliInputJSONArgument, self).__init__(
+            self._operation_object.session)
+
+    def _register_argument_action(self):
+        self._operation_object.session.register(
+            'calling-command', self.add_to_call_parameters)
+        super(CliInputJSONArgument, self)._register_argument_action()
+
+    def add_to_call_parameters(self, call_parameters, parsed_args,
+                               parsed_globals, **kwargs):
+
+        # Check if ``--cli-input-json`` was specified in the command line.
+        input_json = getattr(parsed_args, 'cli_input_json', None)
+        if input_json is not None:
+            # Retrieve the JSON from the file if needed.
+            retrieved_json = get_paramfile(input_json)
+            # Nothing was retrieved from the file. So assume the argument
+            # is already a JSON string.
+            if retrieved_json is None:
+                retrieved_json = input_json
+            try:
+                # Try to load the JSON string into a python dictionary
+                input_data = json.loads(retrieved_json)
+            except ValueError as e:
+                raise ParamError(
+                    self.name, "Invalid JSON: %s\nJSON received: %s"
+                    % (e, retrieved_json))
+            # Add the members from the input JSON to the call parameters.
+            self._update_call_parameters(call_parameters, input_data)
+
+    def _update_call_parameters(self, call_parameters, input_data):
+        for input_key in input_data.keys():
+            # Only add the values to ``call_parameters`` if not already
+            # present.
+            if input_key not in call_parameters:
+                call_parameters[input_key] = input_data[input_key]
diff --git a/awscli/customizations/cloudsearchdomain.py b/awscli/customizations/cloudsearchdomain.py
index c21ce9d151c0..5aebcaf7fbd1 100644
--- a/awscli/customizations/cloudsearchdomain.py
+++ b/awscli/customizations/cloudsearchdomain.py
@@ -19,11 +19,11 @@
 """
 
 def register_cloudsearchdomain(cli):
-    cli.register('operation-args-parsed.cloudsearchdomain',
-                 validate_endpoint_url)
+    cli.register_last('calling-command.cloudsearchdomain',
+                      validate_endpoint_url)
 
 
 def validate_endpoint_url(parsed_globals, **kwargs):
     if parsed_globals.endpoint_url is None:
-        raise ValueError(
+        return ValueError(
             "--endpoint-url is required for cloudsearchdomain commands")
diff --git a/awscli/customizations/ec2addcount.py b/awscli/customizations/ec2addcount.py
index e16a881e923d..d96cd3d44835 100644
--- a/awscli/customizations/ec2addcount.py
+++ b/awscli/customizations/ec2addcount.py
@@ -40,6 +40,7 @@ def __init__(self, operation, name):
         self.argument_model = model.Shape('CountArgument', {'type': 'string'})
         self._operation = operation
         self._name = name
+        self._required = False
 
     @property
     def cli_name(self):
@@ -51,7 +52,11 @@ def cli_type_name(self):
 
     @property
     def required(self):
-        return False
+        return self._required
+
+    @required.setter
+    def required(self, value):
+        self._required = value
 
     @property
     def documentation(self):
diff --git a/awscli/customizations/ec2decryptpassword.py b/awscli/customizations/ec2decryptpassword.py
index 64f775efa67c..0a27a347acb7 100644
--- a/awscli/customizations/ec2decryptpassword.py
+++ b/awscli/customizations/ec2decryptpassword.py
@@ -45,6 +45,7 @@ def __init__(self, operation, name):
         self._operation = operation
         self._name = name
         self._key_path = None
+        self._required = False
 
     @property
     def cli_type_name(self):
@@ -52,7 +53,11 @@ def cli_type_name(self):
 
     @property
     def required(self):
-        return False
+        return self._required
+
+    @required.setter
+    def required(self, value):
+        self._required = value
 
     @property
     def documentation(self):
diff --git a/awscli/customizations/generatecliskeleton.py b/awscli/customizations/generatecliskeleton.py
new file mode 100644
index 000000000000..8edb7822cdd3
--- /dev/null
+++ b/awscli/customizations/generatecliskeleton.py
@@ -0,0 +1,87 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import json
+import sys
+
+from botocore.utils import ArgumentGenerator
+
+from awscli.customizations.arguments import OverrideRequiredArgsArgument
+
+
+def register_generate_cli_skeleton(cli):
+    cli.register('building-argument-table', add_generate_skeleton)
+
+
+def add_generate_skeleton(operation, argument_table, **kwargs):
+    # This argument cannot support operations with streaming output which
+    # is designated by the argument name `outfile`.
+    if 'outfile' not in argument_table:
+        generate_cli_skeleton_argument = GenerateCliSkeletonArgument(operation)
+        generate_cli_skeleton_argument.add_to_arg_table(argument_table)
+
+
+class GenerateCliSkeletonArgument(OverrideRequiredArgsArgument):
+    """This argument writes a generated JSON skeleton to stdout
+
+    The argument, if present in the command line, will prevent the intended
+    command from taking place. Instead, it will generate a JSON skeleton and
+    print it to standard output. This JSON skeleton then can be filled out
+    and can be used as input to ``--input-cli-json`` in order to run the
+    command with the filled out JSON skeleton.
+    """
+    ARG_DATA = {
+        'name': 'generate-cli-skeleton',
+        'help_text': 'Prints a sample input JSON to standard output. Note the '
+                     'specified operation is not run if this argument is '
+                     'specified. The sample input can be used as an argument '
+                     'for ``--cli-input-json``.',
+        'action': 'store_true',
+        'group_name': 'generate_cli_skeleton'
+    }
+
+    def __init__(self, operation_object):
+        self._operation_object = operation_object
+        super(GenerateCliSkeletonArgument, self).__init__(
+            self._operation_object.session)
+
+    def _register_argument_action(self):
+        self._operation_object.session.register(
+            'calling-command.*', self.generate_json_skeleton)
+        super(GenerateCliSkeletonArgument, self)._register_argument_action()
+
+    def generate_json_skeleton(self, call_parameters, parsed_args,
+                               parsed_globals, **kwargs):
+
+        # Only perform the method if the ``--generate-cli-skeleton`` was
+        # included in the command line.
+        if getattr(parsed_args, 'generate_cli_skeleton', False):
+
+            # Obtain the model of the operation
+            operation_model = self._operation_object.model
+
+            # Generate the skeleton based on the ``input_shape``.
+            argument_generator = ArgumentGenerator()
+            operation_input_shape = operation_model.input_shape
+            # If the ``input_shape`` is ``None``, generate an empty
+            # dictionary.
+            if operation_input_shape is None:
+                skeleton = {}
+            else:
+                skeleton = argument_generator.generate_skeleton(
+                    operation_input_shape)
+
+            # Write the generated skeleton to standard output.
+            sys.stdout.write(json.dumps(skeleton, indent=4))
+            sys.stdout.write('\n')
+            # This is the return code
+            return 0
diff --git a/awscli/customizations/paginate.py b/awscli/customizations/paginate.py
index cfc6891092bb..fe2e84e0181d 100644
--- a/awscli/customizations/paginate.py
+++ b/awscli/customizations/paginate.py
@@ -140,6 +140,7 @@ def __init__(self, name, documentation, operation, parse_type):
         self._name = name
         self._documentation = documentation
         self._parse_type = parse_type
+        self._required = False
 
     @property
     def cli_name(self):
@@ -151,7 +152,11 @@ def cli_type_name(self):
 
     @property
     def required(self):
-        return False
+        return self._required
+
+    @required.setter
+    def required(self, value):
+        self._required = value
 
     @property
     def documentation(self):
diff --git a/awscli/customizations/streamingoutputarg.py b/awscli/customizations/streamingoutputarg.py
index 8f61c139915e..536e640e64c2 100644
--- a/awscli/customizations/streamingoutputarg.py
+++ b/awscli/customizations/streamingoutputarg.py
@@ -52,6 +52,7 @@ def __init__(self, response_key, operation, name, buffer_size=None):
         self._response_key = response_key
         self._output_file = None
         self._name = name
+        self._required = True
 
     @property
     def cli_name(self):
@@ -66,7 +67,11 @@ def cli_type_name(self):
 
     @property
     def required(self):
-        return True
+        return self._required
+
+    @required.setter
+    def required(self, value):
+        self._required = value
 
     @property
     def documentation(self):
diff --git a/awscli/handlers.py b/awscli/handlers.py
index 7eceb4ebcda3..e9d2ce941f60 100644
--- a/awscli/handlers.py
+++ b/awscli/handlers.py
@@ -48,6 +48,9 @@
 from awscli.customizations.cloudsearchdomain import register_cloudsearchdomain
 from awscli.customizations.s3endpoint import register_s3_endpoint
 from awscli.customizations.s3errormsg import register_s3_error_msg
+from awscli.customizations.cliinputjson import register_cli_input_json
+from awscli.customizations.generatecliskeleton import \
+    register_generate_cli_skeleton
 
 
 def awscli_initialize(event_handlers):
@@ -70,6 +73,7 @@ def awscli_initialize(event_handlers):
 #                            param_shorthand.add_example_fn)
     event_handlers.register('doc-examples.*.*',
                             add_examples)
+    register_cli_input_json(event_handlers)
     event_handlers.register('building-argument-table.s3api.*',
                             add_streaming_output_arg)
     event_handlers.register('building-argument-table.ec2.run-instances',
@@ -100,3 +104,4 @@ def awscli_initialize(event_handlers):
     emr_initialize(event_handlers)
     register_cloudsearchdomain(event_handlers)
     register_s3_endpoint(event_handlers)
+    register_generate_cli_skeleton(event_handlers)
diff --git a/tests/integration/customizations/test_cliinputjson.py b/tests/integration/customizations/test_cliinputjson.py
new file mode 100644
index 000000000000..d5538a204af8
--- /dev/null
+++ b/tests/integration/customizations/test_cliinputjson.py
@@ -0,0 +1,118 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import os
+import time
+import tempfile
+import random
+import shutil
+
+import botocore.session
+
+from awscli.testutils import unittest, aws
+
+
+class TestIntegCliInputJson(unittest.TestCase):
+    """This tests to see if a service properly uses the generated input JSON.
+
+    The s3 service was chosen becuase its operations do not take a lot of time.
+    These tests are essentially smoke tests. They are testing that the
+    ``--cli-input-json`` works. It is by no means exhaustive.
+    """
+    def setUp(self):
+        self.session = botocore.session.get_session()
+        self.region = 'us-west-2'
+
+        # Set up a s3 bucket.
+        self.s3 = self.session.create_client('s3', region_name=self.region)
+        self.bucket_name = 'cliinputjsontest%s-%s' % (
+            int(time.time()), random.randint(1, 1000000))
+        self.s3.create_bucket(
+            Bucket=self.bucket_name,
+            CreateBucketConfiguration={'LocationConstraint': self.region}
+        )
+
+        # Add an object to the bucket.
+        self.obj_name = 'foo'
+        self.s3.put_object(
+            Bucket=self.bucket_name,
+            Key=self.obj_name,
+            Body='bar'
+        )
+
+        # Create a temporary sample input json file.
+        self.input_json = '{"Bucket": "%s", "Key": "%s"}' % \
+            (self.bucket_name, self.obj_name)
+
+        self.temp_dir = tempfile.mkdtemp()
+        self.temp_file = os.path.join(self.temp_dir, 'foo.json')
+        with open(self.temp_file, 'w') as f:
+            f.write(self.input_json)
+
+    def tearDown(self):
+        shutil.rmtree(self.temp_dir)
+        self.s3.delete_object(
+            Bucket=self.bucket_name,
+            Key=self.obj_name
+        )
+        self.s3.delete_bucket(Bucket=self.bucket_name)
+
+    def test_cli_input_json_no_exta_args(self):
+        # Run a head command using the input json
+        p = aws('s3api head-object --cli-input-json file://%s --region %s'
+                % (self.temp_file, self.region))
+        # The head object command should find the object specified by the
+        # input json file.
+        self.assertEqual(p.rc, 0)
+
+    def test_cli_input_json_exta_args(self):
+        # Check that the object can be found.
+        p = aws('s3api head-object --cli-input-json file://%s --region %s'
+                % (self.temp_file, self.region))
+        self.assertEqual(p.rc, 0)
+
+        # Override the ``key`` argument. Should produce a failure because
+        # the key ``bar`` does not exist.
+        p = aws('s3api head-object --key bar --cli-input-json file://%s '
+                '--region %s'
+                % (self.temp_file, self.region))
+        self.assertEqual(p.rc, 255)
+        self.assertIn('Not Found', p.stderr)
+
+    def test_cli_input_json_not_from_file(self):
+        # Check that the input json can be used without having to use a file.
+        p = aws(
+            's3api head-object --region %s --cli-input-json '
+            '\'{"Bucket": "%s", "Key": "%s"}\'' %
+            (self.region, self.bucket_name, self.obj_name))
+        self.assertEqual(p.rc, 0)
+
+    def test_cli_input_json_missing_required(self):
+        # Check that the operation properly throws an error if the json is
+        # missing any required arguments and the argument is not on the
+        # command line.
+        p = aws(
+            's3api head-object --region %s --cli-input-json '
+            '\'{"Key": "%s"}\'' %
+            (self.region, self.obj_name))
+        self.assertEqual(p.rc, 255)
+        self.assertIn('Missing', p.stderr)
+
+    def test_cli_input_json_has_extra_unknown_args(self):
+        # Check that the operation properly throws an error if the json
+        # has an extra argument that is not defined by the model.
+        p = aws(
+            's3api head-object --region %s --cli-input-json '
+            '\'{"Bucket": "%s", "Key": "%s", "Foo": "bar"}\'' %
+            (self.region, self.bucket_name, self.obj_name))
+        self.assertEqual(p.rc, 255)
+        self.assertIn('Unknown', p.stderr)
diff --git a/tests/integration/customizations/test_generatecliskeleton.py b/tests/integration/customizations/test_generatecliskeleton.py
new file mode 100644
index 000000000000..b089c001a098
--- /dev/null
+++ b/tests/integration/customizations/test_generatecliskeleton.py
@@ -0,0 +1,84 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import json
+
+from nose.tools import assert_equal
+
+from awscli.clidriver import create_clidriver
+from awscli.testutils import unittest, aws
+
+
+def test_can_generate_skeletons_for_all_service_comands():
+    driver = create_clidriver()
+    help_command = driver.create_help_command()
+    for command_name, command_obj in help_command.command_table.items():
+        sub_help = command_obj.create_help_command()
+        # This avoids command objects like ``PreviewModeCommand`` that
+        # do not exhibit any visible functionality (i.e. provides a command
+        # for the CLI).
+        if hasattr(sub_help, 'command_table'):
+            for sub_name, sub_command in sub_help.command_table.items():
+                op_help = sub_command.create_help_command()
+                arg_table = op_help.arg_table
+                if 'generate-cli-skeleton' in arg_table:
+                    yield _test_gen_skeleton, command_name, sub_name
+
+
+def _test_gen_skeleton(command_name, operation_name):
+    p = aws('%s %s --generate-cli-skeleton' % (command_name, operation_name))
+    assert_equal(p.rc, 0, 'Received non zero RC (%s) for command: %s %s'
+                 % (p.rc, command_name, operation_name))
+    try:
+        parsed = json.loads(p.stdout)
+    except ValueError as e:
+        raise AssertionError(
+            "Could not generate CLI skeleton for command: %s %s\n"
+            "stdout:\n%s\n"
+            "stderr:\n%s\n" % (command_name, operation_name))
+
+
+class TestIntegGenerateCliSkeleton(unittest.TestCase):
+    """This tests various services to see if the generated skeleton is correct
+
+    The operations and services selected are arbitrary. Tried to pick
+    operations that do not have many input options for the sake of readablity
+    and maintenance. These are essentially smoke tests. It is not trying to
+    test the different types of input shapes that can be generated in the
+    skeleton. It is only testing wheter the skeleton generator argument works
+    for various services.
+    """
+    def test_generate_cli_skeleton_s3api(self):
+        p = aws('s3api delete-object --generate-cli-skeleton')
+        self.assertEqual(p.rc, 0)
+        self.assertEqual(
+            p.stdout,
+            '{\n    "Bucket": "", \n    "Key": "", \n    "MFA": "", \n    '
+            '"VersionId": ""\n}\n'
+        )
+
+    def test_generate_cli_skeleton_sqs(self):
+        p = aws('sqs change-message-visibility --generate-cli-skeleton')
+        self.assertEqual(p.rc, 0)
+        self.assertEqual(
+            p.stdout,
+            '{\n    "QueueUrl": "", \n    "ReceiptHandle": "", \n    '
+            '"VisibilityTimeout": 0\n}\n'
+        )
+
+    def test_generate_cli_skeleton_iam(self):
+        p = aws('iam create-group --generate-cli-skeleton')
+        self.assertEqual(p.rc, 0)
+        self.assertEqual(
+            p.stdout,
+            '{\n    "Path": "", \n    "GroupName": ""\n}\n'
+        )
diff --git a/tests/unit/customizations/test_arguments.py b/tests/unit/customizations/test_arguments.py
new file mode 100644
index 000000000000..ade75bf70545
--- /dev/null
+++ b/tests/unit/customizations/test_arguments.py
@@ -0,0 +1,45 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import mock
+
+from awscli.testutils import unittest
+from awscli.customizations.arguments import OverrideRequiredArgsArgument
+
+
+class TestOverrideRequiredArgsArgument(unittest.TestCase):
+    def setUp(self):
+        self.session = mock.Mock()
+        self.argument = OverrideRequiredArgsArgument(self.session)
+
+        # Set up a sample argument_table
+        self.argument_table = {}
+        self.mock_arg = mock.Mock()
+        self.mock_arg.required = True
+        self.argument_table['mock-arg'] = self.mock_arg
+
+    def test_register_argument_action(self):
+        register_args = self.session.register.call_args
+        self.assertEqual(register_args[0][0],
+                         'before-building-argument-table-parser')
+        self.assertEqual(register_args[0][1],
+                         self.argument.override_required_args)
+
+    def test_override_required_args_if_in_cmdline(self):
+        args = ['--no-required-args']
+        self.argument.override_required_args(self.argument_table, args)
+        self.assertFalse(self.mock_arg.required)
+
+    def test_no_override_required_args_if_not_in_cmdline(self):
+        args = []
+        self.argument.override_required_args(self.argument_table, args)
+        self.assertTrue(self.mock_arg.required)
diff --git a/tests/unit/customizations/test_cliinputjson.py b/tests/unit/customizations/test_cliinputjson.py
new file mode 100644
index 000000000000..b8c0240357bc
--- /dev/null
+++ b/tests/unit/customizations/test_cliinputjson.py
@@ -0,0 +1,101 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import mock
+import os
+import shutil
+import tempfile
+
+from awscli.testutils import unittest
+from awscli.argprocess import ParamError
+from awscli.customizations.cliinputjson import CliInputJSONArgument
+
+
+class TestCliInputJSONArgument(unittest.TestCase):
+    def setUp(self):
+        self.operation_object = mock.Mock()
+        self.argument = CliInputJSONArgument(self.operation_object)
+
+        # Create the various forms the data could come in. The two main forms
+        # are as a string and or as a path to a file.
+        self.input_json = '{"A": "foo", "B": "bar"}'
+
+        # Make a temporary file
+        self.temp_dir = tempfile.mkdtemp()
+        self.temp_file = os.path.join(self.temp_dir, 'foo.json')
+        with open(self.temp_file, 'w') as f:
+            f.write(self.input_json)
+
+    def tearDown(self):
+        shutil.rmtree(self.temp_dir)
+
+    def test_register_argument_action(self):
+        register_args = self.operation_object.session.register.call_args_list
+        self.assertEqual(register_args[0][0][0], 'calling-command')
+        self.assertEqual(register_args[0][0][1],
+                         self.argument.add_to_call_parameters)
+
+    def test_add_to_call_parameters_no_file(self):
+        parsed_args = mock.Mock()
+        # Make the value a JSON string
+        parsed_args.cli_input_json = self.input_json
+        call_parameters = {}
+        self.argument.add_to_call_parameters(
+            service_operation=None, call_parameters=call_parameters,
+            parsed_args=parsed_args, parsed_globals=None
+        )
+        self.assertEqual(call_parameters, {'A': 'foo', 'B': 'bar'})
+
+    def test_add_to_call_parameters_with_file(self):
+        parsed_args = mock.Mock()
+        # Make the value a file with JSON located inside.
+        parsed_args.cli_input_json = 'file://' + self.temp_file
+        call_parameters = {}
+        self.argument.add_to_call_parameters(
+            service_operation=None, call_parameters=call_parameters,
+            parsed_args=parsed_args, parsed_globals=None
+        )
+        self.assertEqual(call_parameters, {'A': 'foo', 'B': 'bar'})
+
+    def test_add_to_call_parameters_bad_json(self):
+        parsed_args = mock.Mock()
+        # Create a bad JSON input
+        parsed_args.cli_input_json = self.input_json + ','
+        call_parameters = {}
+        with self.assertRaises(ParamError):
+            self.argument.add_to_call_parameters(
+                service_operation=None, call_parameters=call_parameters,
+                parsed_args=parsed_args, parsed_globals=None
+            )
+
+    def test_add_to_call_parameters_no_clobber(self):
+        parsed_args = mock.Mock()
+        parsed_args.cli_input_json = self.input_json
+        # The value for ``A`` should not be clobbered by the input JSON
+        call_parameters = {'A': 'baz'}
+        self.argument.add_to_call_parameters(
+            service_operation=None, call_parameters=call_parameters,
+            parsed_args=parsed_args, parsed_globals=None
+        )
+        self.assertEqual(call_parameters, {'A': 'baz', 'B': 'bar'})
+
+    def test_no_add_to_call_parameters(self):
+        parsed_args = mock.Mock()
+        parsed_args.cli_input_json = None
+        call_parameters = {'A': 'baz'}
+        self.argument.add_to_call_parameters(
+            service_operation=None, call_parameters=call_parameters,
+            parsed_args=parsed_args, parsed_globals=None
+        )
+        # Nothing should have been added to the call parameters because
+        # ``cli_input_json`` is not in the ``parsed_args``
+        self.assertEqual(call_parameters, {'A': 'baz'})
diff --git a/tests/unit/customizations/test_cloudsearchdomain.py b/tests/unit/customizations/test_cloudsearchdomain.py
index e545af32fd41..04eb230f423c 100644
--- a/tests/unit/customizations/test_cloudsearchdomain.py
+++ b/tests/unit/customizations/test_cloudsearchdomain.py
@@ -59,8 +59,9 @@ class TestCloudsearchDomainHandler(unittest.TestCase):
     def test_validate_endpoint_url_is_none(self):
         parsed_globals = mock.Mock()
         parsed_globals.endpoint_url = None
-        with self.assertRaises(ValueError):
-            validate_endpoint_url(parsed_globals)
+        # Method should return instantiated exception.
+        self.assertTrue(isinstance(validate_endpoint_url(parsed_globals),
+                                   ValueError))
 
 
 if __name__ == "__main__":
diff --git a/tests/unit/customizations/test_generatecliskeleton.py b/tests/unit/customizations/test_generatecliskeleton.py
new file mode 100644
index 000000000000..161ee9c40a55
--- /dev/null
+++ b/tests/unit/customizations/test_generatecliskeleton.py
@@ -0,0 +1,96 @@
+# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+import six
+import mock
+
+from botocore.model import DenormalizedStructureBuilder
+
+from awscli.testutils import unittest
+from awscli.customizations.generatecliskeleton import \
+    GenerateCliSkeletonArgument
+
+
+class TestGenerateCliSkeleton(unittest.TestCase):
+    def setUp(self):
+        self.operation_object = mock.Mock()
+        self.argument = GenerateCliSkeletonArgument(self.operation_object)
+
+        # Create a mock service operation object
+        self.service_operation = mock.Mock()
+
+        # Make an arbitrary input model shape.
+        self.input_shape = {
+            'A': {
+                'type': 'structure',
+                'members': {
+                    'B': {'type': 'string'},
+                }
+            }
+        }
+        shape = DenormalizedStructureBuilder().with_members(
+            self.input_shape).build_model()
+        self.operation_object.model.input_shape = shape
+
+        # This is what the json should should look like after being
+        # generated to standard output.
+        self.ref_json_output = \
+            '{\n    "A": {\n        "B": ""\n    }\n}\n'
+
+    def test_register_argument_action(self):
+        register_args = self.operation_object.session.register.call_args_list
+        self.assertEqual(register_args[0][0][0], 'calling-command.*')
+        self.assertEqual(register_args[0][0][1],
+                         self.argument.generate_json_skeleton)
+
+    def test_generate_json_skeleton(self):
+        parsed_args = mock.Mock()
+        parsed_args.generate_cli_skeleton = True
+        with mock.patch('sys.stdout', six.StringIO()) as mock_stdout:
+            rc = self.argument.generate_json_skeleton(
+                service_operation=self.service_operation, call_parameters=None,
+                parsed_args=parsed_args, parsed_globals=None
+            )
+            # Ensure the contents printed to standard output are correct.
+            self.assertEqual(self.ref_json_output, mock_stdout.getvalue())
+            # Ensure it is the correct return code of zero.
+            self.assertEqual(rc, 0)
+
+    def test_no_generate_json_skeleton(self):
+        parsed_args = mock.Mock()
+        parsed_args.generate_cli_skeleton = False
+        with mock.patch('sys.stdout', six.StringIO()) as mock_stdout:
+            rc = self.argument.generate_json_skeleton(
+                service_operation=self.service_operation, call_parameters=None,
+                parsed_args=parsed_args, parsed_globals=None
+            )
+            # Ensure nothing is printed to standard output
+            self.assertEqual('', mock_stdout.getvalue())
+            # Ensure nothing is returned because it was never called.
+            self.assertEqual(rc, None)
+
+
+    def test_generate_json_skeleton_no_input_shape(self):
+        parsed_args = mock.Mock()
+        parsed_args.generate_cli_skeleton = True
+        # Set the input shape to ``None``.
+        self.operation_object.model.input_shape = None
+        with mock.patch('sys.stdout', six.StringIO()) as mock_stdout:
+            rc = self.argument.generate_json_skeleton(
+                service_operation=self.service_operation, call_parameters=None,
+                parsed_args=parsed_args, parsed_globals=None
+            )
+            # Ensure the contents printed to standard output are correct,
+            # which should be an empty dictionary.
+            self.assertEqual('{}\n', mock_stdout.getvalue())
+            # Ensure it is the correct return code of zero.
+            self.assertEqual(rc, 0)
diff --git a/tests/unit/test_clidriver.py b/tests/unit/test_clidriver.py
index 9c1512f617c0..4e16303d2142 100644
--- a/tests/unit/test_clidriver.py
+++ b/tests/unit/test_clidriver.py
@@ -245,10 +245,12 @@ def test_expected_events_are_emitted_in_order(self):
             'top-level-args-parsed',
             'building-command-table.s3',
             'building-argument-table.s3.list-objects',
+            'before-building-argument-table-parser.s3.list-objects',
             'operation-args-parsed.s3.list-objects',
             'load-cli-arg.s3.list-objects.bucket',
             'process-cli-arg.s3.list-objects',
             'load-cli-arg.s3.list-objects.key',
+            'calling-command.s3.list-objects'
         ])
 
     def test_create_help_command(self):
@@ -619,6 +621,34 @@ def raise_exception(*args, **kwargs):
             'Unable to locate credentials. '
             'You can configure credentials by running "aws configure".')
 
+    def test_override_calling_command(self):
+        self.driver = create_clidriver()
+
+        # Make a function that will return an override such that its value
+        # is used over whatever is returned by the invoker which is usually
+        # zero.
+        def override_with_rc(**kwargs):
+            return 20
+
+        self.driver.session.register('calling-command', override_with_rc)
+        rc = self.driver.main('ec2 describe-instances'.split())
+        # Check that the overriden rc is as expected.
+        self.assertEqual(rc, 20)
+
+    def test_override_calling_command_error(self):
+        self.driver = create_clidriver()
+
+        # Make a function that will return an error. The handler will cause
+        # an error to be returned and later raised.
+        def override_with_error(**kwargs):
+            return ValueError()
+
+        self.driver.session.register('calling-command', override_with_error)
+        # An exception should be thrown as a result of the handler, which
+        # will result in 255 rc.
+        rc = self.driver.main('ec2 describe-instances'.split())
+        self.assertEqual(rc, 255)
+
 
 class TestHTTPParamFileDoesNotExist(BaseAWSCommandParamsTest):
 
diff --git a/tests/unit/test_completer.py b/tests/unit/test_completer.py
index bd41e0e0f063..3839171d0b32 100644
--- a/tests/unit/test_completer.py
+++ b/tests/unit/test_completer.py
@@ -62,7 +62,8 @@
      set(['--filters', '--dry-run', '--no-dry-run', '--endpoint-url',
           '--no-verify-ssl', '--no-paginate', '--no-sign-request',
           '--output', '--profile', '--starting-token', '--max-items',
-          '--region', '--version', '--color', '--query', '--page-size'])),
+          '--region', '--version', '--color', '--query', '--page-size',
+          '--generate-cli-skeleton', '--cli-input-json'])),
     ('aws s3', -1, set(['cp', 'mv', 'rm', 'mb', 'rb', 'ls', 'sync', 'website'])),
     ('aws s3 m', -1, set(['mv', 'mb'])),
     ('aws s3 cp -', -1, set(['--no-guess-mime-type', '--dryrun',