Repository: Azure/azure-cli

Issue:
az vm list-usages needs table/tsv work
Currently outputs like:

```
$ az vm list-usage --location westus
  Limit    CurrentValue
-------  --------------
   2000
    100               4
  10000               4
   2000
    100               4
    100
    100
    100
    100
    100
    100
    100
    100
    100
    100
     24
     48
      8
    100
    100
    100
    100

   2000               1
   2000               4
```

But should have a formatter written to provide output like
```
Name                       CurrentValue Limit
------------------- ------------- --------
standardMSFamily                      1    2000   
StandardDiskCount                    0      100
PremiumDiskCount                     1      100
[...]
```

In addition, when the `CurrentValue`values are 0, we should still show the 0 in `table` output, where it seems we suppress the 0 value now (it is in the JSON doc).


Discussion:

Patch:
diff --git a/src/azure-cli-core/azure/cli/core/_profile.py b/src/azure-cli-core/azure/cli/core/_profile.py
index 113eac669..17763237a 100644
--- a/src/azure-cli-core/azure/cli/core/_profile.py
+++ b/src/azure-cli-core/azure/cli/core/_profile.py
@@ -471,7 +471,6 @@ class SubscriptionFinder(object):
                 temp_credentials[_ACCESS_TOKEN])
             all_subscriptions.extend(subscriptions)
 
-        self.tenants = tenants
         return all_subscriptions
 
     def _find_using_specific_tenant(self, tenant, access_token):
@@ -484,7 +483,7 @@ class SubscriptionFinder(object):
         for s in subscriptions:
             setattr(s, 'tenant_id', tenant)
             all_subscriptions.append(s)
-        self.tenants = [tenant]
+        self.tenants.append(tenant)
         return all_subscriptions
 
 
diff --git a/src/command_modules/azure-cli-interactive/azclishell/_dump_commands.py b/src/command_modules/azure-cli-interactive/azclishell/_dump_commands.py
index c1ccb67af..6fc3d1c26 100644
--- a/src/command_modules/azure-cli-interactive/azclishell/_dump_commands.py
+++ b/src/command_modules/azure-cli-interactive/azclishell/_dump_commands.py
@@ -14,6 +14,7 @@ import yaml
 from azure.cli.core.application import APPLICATION, Configuration
 from azure.cli.core.commands import _update_command_definitions, BLACKLISTED_MODS
 from azure.cli.core.help_files import helps
+from azure.cli.core.commands.arm import add_id_parameters
 
 import azclishell.configuration as config
 
@@ -48,6 +49,7 @@ def dump_command_table():
     command_file = config.CONFIGURATION.get_help_files()
 
     install_modules()
+    add_id_parameters(CMD_TABLE)
 
     data = {}
     for cmd in CMD_TABLE:
diff --git a/src/command_modules/azure-cli-interactive/azclishell/app.py b/src/command_modules/azure-cli-interactive/azclishell/app.py
index 405bf53e3..6b653c00d 100644
--- a/src/command_modules/azure-cli-interactive/azclishell/app.py
+++ b/src/command_modules/azure-cli-interactive/azclishell/app.py
@@ -554,8 +554,7 @@ class Shell(object):
             CONFIG.load(os.path.join(azure_folder, 'az.json'))
             SESSION.load(os.path.join(azure_folder, 'az.sess'), max_age=3600)
 
-            config = Configuration()
-            self.app.initialize(config)
+            self.app.initialize(Configuration())
 
             result = self.app.execute(args)
             self.last_exit = 0
diff --git a/src/command_modules/azure-cli-profile/HISTORY.rst b/src/command_modules/azure-cli-profile/HISTORY.rst
index e0018b776..3eee49a32 100644
--- a/src/command_modules/azure-cli-profile/HISTORY.rst
+++ b/src/command_modules/azure-cli-profile/HISTORY.rst
@@ -6,6 +6,7 @@ Release History
 2.0.5 (unreleased)
 * Output deprecating information on using '--expanded-view'
 * Add get-access-token command to provide raw AAD token
+* Support login with a user account with no associated subscriptions
 
 2.0.4 (2017-04-28)
 ++++++++++++++++++
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/commands.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/commands.py
index 24b237cec..69ca29b28 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/commands.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/commands.py
@@ -64,6 +64,26 @@ def transform_vm_create_output(result):
         return None if isinstance(result, ClientRawResponse) else result
 
 
+def transform_vm_usage_list(result):
+    result = list(result)
+    for item in result:
+        item.current_value = str(item.current_value)
+        item.limit = str(item.limit)
+        item.local_name = item.name.localized_value
+    return result
+
+
+def transform_vm_usage_table(result):
+    transformed = []
+    for item in result:
+        transformed.append(OrderedDict([
+            ('Name', item['localName']),
+            ('CurrentValue', item['currentValue']),
+            ('Limit', item['limit'])
+        ]))
+    return transformed
+
+
 def transform_vm_list(vm_list):
     return [transform_vm(v) for v in vm_list]
 
@@ -217,7 +237,7 @@ cli_command(__name__, 'vm image list-skus', mgmt_path.format(op_var, op_class, '
 cli_command(__name__, 'vm image list', custom_path.format('list_vm_images'))
 
 # VM Usage
-cli_command(__name__, 'vm list-usage', mgmt_path.format('usage_operations', 'UsageOperations', 'list'), cf_usage)
+cli_command(__name__, 'vm list-usage', mgmt_path.format('usage_operations', 'UsageOperations', 'list'), cf_usage, transform=transform_vm_usage_list, table_transformer=transform_vm_usage_table)
 
 # VMSS
 cli_command(__name__, 'vmss delete', mgmt_path.format('virtual_machine_scale_sets_operations', 'VirtualMachineScaleSetsOperations', 'delete'), cf_vmss, no_wait_param='raw')


Test patch:
diff --git a/src/azure-cli-core/tests/test_profile.py b/src/azure-cli-core/tests/test_profile.py
index ad9d05766..0b11ec681 100644
--- a/src/azure-cli-core/tests/test_profile.py
+++ b/src/azure-cli-core/tests/test_profile.py
@@ -255,7 +255,7 @@ class Test_Profile(unittest.TestCase):  # pylint: disable=too-many-public-method
                          extended_info['endpoints'].active_directory)
 
     @mock.patch('adal.AuthenticationContext', autospec=True)
-    def test_create_account_without_subscriptions(self, mock_auth_context):
+    def test_create_account_without_subscriptions_thru_service_principal(self, mock_auth_context):
         mock_auth_context.acquire_token_with_client_credentials.return_value = self.token_entry1
         mock_arm_client = mock.MagicMock()
         mock_arm_client.subscriptions.list.return_value = []
@@ -277,7 +277,42 @@ class Test_Profile(unittest.TestCase):  # pylint: disable=too-many-public-method
                                                      subscription_finder=finder)
 
         # assert
-        self.assertTrue(1, len(result))
+        self.assertEqual(1, len(result))
+        self.assertEqual(result[0]['id'], self.tenant_id)
+        self.assertEqual(result[0]['state'], 'Enabled')
+        self.assertEqual(result[0]['tenantId'], self.tenant_id)
+        self.assertEqual(result[0]['name'], 'N/A(tenant level account)')
+
+    @mock.patch('adal.AuthenticationContext', autospec=True)
+    def test_create_account_without_subscriptions_thru_common_tenant(self, mock_auth_context):
+        mock_auth_context.acquire_token.return_value = self.token_entry1
+        mock_auth_context.acquire_token_with_username_password.return_value = self.token_entry1
+        tenant_object = mock.MagicMock()
+        tenant_object.id = "foo-bar"
+        tenant_object.tenant_id = self.tenant_id
+        mock_arm_client = mock.MagicMock()
+        mock_arm_client.subscriptions.list.return_value = []
+        mock_arm_client.tenants.list.return_value = (x for x in [tenant_object])
+
+        finder = SubscriptionFinder(lambda _, _2: mock_auth_context,
+                                    None,
+                                    lambda _: mock_arm_client)
+
+        storage_mock = {'subscriptions': []}
+        profile = Profile(storage_mock, use_global_creds_cache=False)
+        profile._management_resource_uri = 'https://management.core.windows.net/'
+
+        # action
+        result = profile.find_subscriptions_on_login(False,
+                                                     '1234',
+                                                     'my-secret',
+                                                     False,
+                                                     None,
+                                                     allow_no_subscriptions=True,
+                                                     subscription_finder=finder)
+
+        # assert
+        self.assertEqual(1, len(result))
         self.assertEqual(result[0]['id'], self.tenant_id)
         self.assertEqual(result[0]['state'], 'Enabled')
         self.assertEqual(result[0]['tenantId'], self.tenant_id)