Repository: aws/aws-cli

Issue:
'dict' object has no attribute 'startswith' in cloudformation package
After updating to the latest version of the aws cli (1.16.144), CI/CD processes that had been stable for over a year are now consistently failing with the following error:

'dict' object has no attribute 'startswith'

when running:

aws cloudformation package --template-file ./foo.yaml --output-template-file ./bar.yaml --s3-bucket foo --s3-prefix bar --region us-east-1


Discussion:
I have recently run into this issue too.  Any ideas?
Temporary work around:

pip install awscli==1.16.141"  instead of "- pip install --upgrade awscli"
I made your recommended change to our CodePipeline template file and this has solved our issue.

Thanks KarlGrear.
Having a similar issue and looks like I can use 1.16.143.  Stack trace if it helps:
```
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/awscli/clidriver.py", line 207, in main
    return command_table[parsed_args.command](remaining, parsed_args)
  File "/usr/local/lib/python3.7/site-packages/awscli/clidriver.py", line 348, in __call__
    return command_table[parsed_args.operation](remaining, parsed_globals)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/commands.py", line 187, in __call__
    return self._run_main(parsed_args, parsed_globals)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/package.py", line 151, in _run_main
    exported_str = self._export(template_path, use_json)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/package.py", line 167, in _export
    exported_template = template.export()
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/artifact_exporter.py", line 604, in export
    self.template_dict = self.export_global_artifacts(self.template_dict)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/artifact_exporter.py", line 563, in export_global_artifacts
    self.export_global_artifacts(val)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/artifact_exporter.py", line 563, in export_global_artifacts
    self.export_global_artifacts(val)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/artifact_exporter.py", line 563, in export_global_artifacts
    self.export_global_artifacts(val)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/artifact_exporter.py", line 561, in export_global_artifacts
    template_dict[key] = GLOBAL_EXPORT_DICT[key](val, self.uploader, self.template_dir)
  File "/usr/local/lib/python3.7/site-packages/awscli/customizations/cloudformation/artifact_exporter.py", line 506, in include_transform_export_handler
    abs_include_location = os.path.join(parent_dir, include_location)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/posixpath.py", line 94, in join
    genericpath._check_arg_types('join', a, *p)
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/genericpath.py", line 149, in _check_arg_types
    (funcname, s.__class__.__name__)) from None
TypeError: join() argument must be str or bytes, not 'OrderedDict'
2019-04-23 15:36:45,172 - MainThread - awscli.clidriver - DEBUG - Exiting with rc 255
```
Will this be fixed for 1.16.145?
I'm also seeing the same issue. Unfortunately my CI provider doesn't make it easy to target specific AWS CLI versions and this is proving really unfortunate 
Cross referencing #4099 and looping in @sanathkr.
Working on a fix for this now..
Having this same issue, using the latest version of the AWS CLI, and the latest version of the SAM CLI. Running on this docker image `lambci/lambda:build-dotnetcore2.1`

```
bash-4.2# aws --version
aws-cli/1.16.158 Python/2.7.16 Linux/4.9.125-linuxkit exec-env/AWS_Lambda_dotnetcore2.1 botocore/1.12.148
bash-4.2# sam --version
SAM CLI, version 0.16.0
```

Getting this error when using `sam package` or `aws cloudformation package`
```
'OrderedDict' object has no attribute 'startswith'
```

Edit:

Using @KarlGrear 's above recommendation worked for me as a temporary work around.
aws-cli/1.16.144 Python/2.7.15 Darwin/18.5.0 botocore/1.12.134
SAM CLI, version 0.15.0

@KarlGrear workaround also works for me
Hello, any idea of a timeframe for fixing this? We're in a situation where we are only able to use the latest version of the cli provided by AWS. This is blocking our ability to deploy.

Resolving patch:
diff --git a/awscli/customizations/cloudformation/artifact_exporter.py b/awscli/customizations/cloudformation/artifact_exporter.py
--- a/awscli/customizations/cloudformation/artifact_exporter.py
+++ b/awscli/customizations/cloudformation/artifact_exporter.py
@@ -516,9 +516,13 @@ def include_transform_export_handler(template_dict, uploader, parent_dir):
         return template_dict
 
     include_location = template_dict.get("Parameters", {}).get("Location", None)
-    if not include_location or is_s3_url(include_location):
+    if not include_location \
+            or not is_path_value_valid(include_location) \
+            or is_s3_url(include_location):
+        # `include_location` is either empty, or not a string, or an S3 URI
         return template_dict
 
+    # We are confident at this point that `include_location` is a string containing the local path
     abs_include_location = os.path.join(parent_dir, include_location)
     if is_local_file(abs_include_location):
         template_dict["Parameters"]["Location"] = uploader.upload_with_dedup(abs_include_location)


Test patch:
diff --git a/tests/unit/customizations/cloudformation/test_artifact_exporter.py b/tests/unit/customizations/cloudformation/test_artifact_exporter.py
--- a/tests/unit/customizations/cloudformation/test_artifact_exporter.py
+++ b/tests/unit/customizations/cloudformation/test_artifact_exporter.py
@@ -148,7 +148,7 @@ def test_all_resources_export():
         {
             "class": GlueJobCommandScriptLocationResource,
             "expected_result": {
-                    "ScriptLocation": uploaded_s3_url 
+                    "ScriptLocation": uploaded_s3_url
             }
         }
     ]
@@ -1097,6 +1097,20 @@ def test_include_transform_export_handler_with_no_path(self, is_local_file_mock)
         is_local_file_mock.assert_not_called()
         self.s3_uploader_mock.assert_not_called()
 
+    @patch("awscli.customizations.cloudformation.artifact_exporter.is_local_file")
+    def test_include_transform_export_handler_with_dict_value_for_location(self, is_local_file_mock):
+
+        handler_output = include_transform_export_handler(
+            {"Name": "AWS::Include", "Parameters": {"Location": {"Fn::Sub": "${S3Bucket}/file.txt"}}},
+            self.s3_uploader_mock,
+            "parent_dir")
+        # Input is returned unmodified
+        self.assertEquals(handler_output, {"Name": "AWS::Include", "Parameters": {"Location": {"Fn::Sub": "${S3Bucket}/file.txt"}}})
+
+        is_local_file_mock.assert_not_called()
+        self.s3_uploader_mock.assert_not_called()
+
+
     @patch("awscli.customizations.cloudformation.artifact_exporter.is_local_file")
     def test_include_transform_export_handler_non_local_file(self, is_local_file_mock):
         #returns unchanged template dict if transform not a local file, and not a S3 URI