Repository: Azure/azure-cli

Issue:
Misleading error message when resetting SSH key on a VM
### Description

The following command works on all my VMs except for one particular one show below.
NOTE: 
`az` is an alias to `docker run --rm -v /Users/$(whoami):/root -it azuresdk/azure-cli-python az`
```sh
$ az vm user update -u moody -g myGroup -n myVM --ssh-key-value "$(cat ~/.ssh/id_rsa.pub)"=""
'NoneType' object has no attribute 'lower'
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/site-packages/azure/cli/main.py", line 36, in main
    cmd_result = APPLICATION.execute(args)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/core/application.py", line 211, in execute
    result = expanded_arg.func(params)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/core/commands/__init__.py", line 351, in __call__
    return self.handler(*args, **kwargs)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/core/commands/__init__.py", line 550, in _execute_command
    reraise(*sys.exc_info())
  File "/usr/local/lib/python3.5/site-packages/six.py", line 686, in reraise
    raise value
  File "/usr/local/lib/python3.5/site-packages/azure/cli/core/commands/__init__.py", line 527, in _execute_command
    result = op(client, **kwargs) if client else op(**kwargs)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/command_modules/vm/custom.py", line 619, in set_user
    return _set_linux_user(vm, resource_group_name, username, password, ssh_key_value, no_wait)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/command_modules/vm/custom.py", line 676, in _set_linux_user
    protected_settings)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/command_modules/vm/custom.py", line 727, in _update_linux_access_extension
    _ACCESS_EXT_HANDLER_NAME)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/command_modules/vm/custom.py", line 751, in _get_extension_instance_name
    if x.type.lower() == full_type_name.lower()), None)
  File "/usr/local/lib/python3.5/site-packages/azure/cli/command_modules/vm/custom.py", line 751, in <genexpr>
    if x.type.lower() == full_type_name.lower()), None)
AttributeError: 'NoneType' object has no attribute 'lower'
```

I tried checking the Azure console and found the following message on this particular VM when resetting SSH key
```
The VM agent is either unavailable, or not installed, which may prevent VMAccess from running.
```

Screenshot
![screen shot 2017-07-10 at 2 21 44 pm](https://user-images.githubusercontent.com/29459870/28033152-250c7f6c-657b-11e7-8098-eede273c66ef.png)

_It would be a lot more useful to see that error in the CLI as well instead of a python dump._

---

### Environment summary

**Install Method:** How did you install the CLI? (e.g. pip, interactive script, apt-get, Docker, MSI, nightly)  
Answer here: Docker

**CLI Version:** What version of the CLI and modules are installed? (Use `az --version`)  
Answer here:
```
azure-cli (2.0.10+dev)

acr (2.0.8+dev)
acs (2.0.10+dev)
appservice (0.1.10+dev)
batch (3.0.3+dev)
billing (0.1.3+dev)
cdn (0.0.6+dev)
cloud (2.0.6+dev)
cognitiveservices (0.1.6+dev)
command-modules-nspkg (2.0.1+dev)
component (2.0.6+dev)
configure (2.0.10+dev)
consumption (0.1.3+dev)
core (2.0.11+dev)
cosmosdb (0.1.10+dev)
dla (0.0.10+dev)
dls (0.0.10+dev)
feedback (2.0.6+dev)
find (0.2.6+dev)
interactive (0.3.6+dev)
iot (0.1.9+dev)
keyvault (2.0.8+dev)
lab (0.0.8+dev)
monitor (0.0.8+dev)
network (2.0.10+dev)
nspkg (3.0.1+dev)
profile (2.0.8+dev)
rdbms (0.0.5+dev)
redis (0.2.7+dev)
resource (2.0.10+dev)
role (2.0.8+dev)
sf (1.0.5+dev)
sql (2.0.7+dev)
storage (2.0.10+dev)
taskhelp (0.1.5+dev)
vm (2.0.10+dev)

Python (Linux) 3.5.2 (default, Dec 27 2016, 21:33:11)
[GCC 5.3.0]

Python location '/usr/local/bin/python'
```

**OS Version:** What OS and version are you using?  
Answer here: macOS Sierra 10.12.5 (16F2104)

**Shell Type:** What shell are you using? (e.g. bash, cmd.exe, Bash on Windows)  
Answer here: zsh



Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=h1) Report
> Merging [#4047](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=desc) into [master](https://codecov.io/gh/Azure/azure-cli/commit/dd91433b36f95e9c849cbbcecccb0f610a8f0a3c?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/Azure/azure-cli/pull/4047/graphs/tree.svg?height=150&width=650&token=2pog0TKvF8&src=pr)](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master    #4047   +/-   ##
=======================================
  Coverage   70.61%   70.61%           
=======================================
  Files         428      428           
  Lines       27871    27871           
  Branches     4267     4267           
=======================================
  Hits        19680    19680           
- Misses       6915     6919    +4     
+ Partials     1276     1272    -4
```


| [Impacted Files](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...zure-cli-vm/azure/cli/command\_modules/vm/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktdm0vYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy92bS9jdXN0b20ucHk=) | `75.45% <ø> (ø)` | :arrow_up: |
| [src/azure-cli-core/azure/cli/core/util.py](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=tree#diff-c3JjL2F6dXJlLWNsaS1jb3JlL2F6dXJlL2NsaS9jb3JlL3V0aWwucHk=) | `70.66% <0%> (ø)` | :arrow_up: |
| [...dback/azure/cli/command\_modules/feedback/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktZmVlZGJhY2svYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy9mZWVkYmFjay9jdXN0b20ucHk=) | `31.25% <0%> (ø)` | :arrow_up: |
| [...nent/azure/cli/command\_modules/component/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktY29tcG9uZW50L2F6dXJlL2NsaS9jb21tYW5kX21vZHVsZXMvY29tcG9uZW50L2N1c3RvbS5weQ==) | `16.23% <0%> (ø)` | :arrow_up: |
| [...li-cloud/azure/cli/command\_modules/cloud/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktY2xvdWQvYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy9jbG91ZC9jdXN0b20ucHk=) | `14.45% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=footer). Last update [dd91433...18754a5](https://codecov.io/gh/Azure/azure-cli/pull/4047?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py
index a0e2fe027..4f08c5271 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py
@@ -751,7 +751,7 @@ def _get_extension_instance_name(instance_view, publisher, extension_type_name,
     full_type_name = '.'.join([publisher, extension_type_name])
     if instance_view.extensions:
         ext = next((x for x in instance_view.extensions
-                    if x.type.lower() == full_type_name.lower()), None)
+                    if x.type and (x.type.lower() == full_type_name.lower())), None)
         if ext:
             extension_instance_name = ext.name
     return extension_instance_name


Test patch:
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_custom_vm_commands.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_custom_vm_commands.py
index 3ddcc7996..58d51f6d9 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_custom_vm_commands.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/tests/test_custom_vm_commands.py
@@ -356,6 +356,18 @@ class Test_Vm_Custom(unittest.TestCase):
         # assert
         self.assertEqual(result, 'extension1')
 
+    def test_get_extension_instance_name_when_type_none(self):
+        instance_view = mock.MagicMock()
+        extension = mock.MagicMock()
+        extension.type = None
+        instance_view.extensions = [extension]
+
+        # action
+        result = _get_extension_instance_name(instance_view, 'na', 'extension-name')
+
+        # assert
+        self.assertEqual(result, 'extension-name')
+
 
 class FakedVM(object):  # pylint: disable=too-few-public-methods
     def __init__(self, nics=None, disks=None, os_disk=None):