Repository: Azure/azure-cli

Issue:
AzureStack: images aliases is not applicable for azurestack
### Description
CLI has a set of aliases for the images referred from the following location
https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json

These aliases makes the assumption that these images are available in all the clouds and matches the 
definition like below
          "UbuntuLTS":{
            "publisher":"Canonical",
            "offer":"UbuntuServer",
            "sku":"16.04-LTS",
            "version":"latest"
          }

In azure stack , the alias does not make much sense as the admin may not have uploaded the same image
in azure stack the Ubuntu server sku could be 14.04.3 instead of 16.04-LTS

luckily , the image parameter can be specified with the whole urn like below and this is what being used in azurestack 
Canonical:UbuntuServer:14.04.3-LTS:1.0.0

All the public documentation is referring to the use of aliases and the aliases most likely wont work against azurestack unless the admin chooses to upload image matching the azure definitions



Discussion:
codecov-io: # [Codecov](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=h1) Report
> Merging [#3754](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=desc) into [master](https://codecov.io/gh/Azure/azure-cli/commit/dff725f8bee017b61cf2d6dc5374f9315e52b644?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff             @@
##           master    #3754      +/-   ##
==========================================
+ Coverage   72.14%   72.15%   +<.01%     
==========================================
  Files         421      421              
  Lines       26010    26018       +8     
  Branches     3943     3945       +2     
==========================================
+ Hits        18765    18773       +8     
- Misses       6026     6029       +3     
+ Partials     1219     1216       -3
```


| [Impacted Files](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...li-cloud/azure/cli/command\_modules/cloud/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktY2xvdWQvYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy9jbG91ZC9jdXN0b20ucHk=) | `18.18% <ø> (ø)` | :arrow_up: |
| [...i-cloud/azure/cli/command\_modules/cloud/\_params.py](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktY2xvdWQvYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy9jbG91ZC9fcGFyYW1zLnB5) | `93.54% <100%> (+0.21%)` | :arrow_up: |
| [...re-cli-vm/azure/cli/command\_modules/vm/\_actions.py](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktdm0vYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy92bS9fYWN0aW9ucy5weQ==) | `76.19% <100%> (+1.19%)` | :arrow_up: |
| [src/azure-cli-core/azure/cli/core/cloud.py](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=tree#diff-c3JjL2F6dXJlLWNsaS1jb3JlL2F6dXJlL2NsaS9jb3JlL2Nsb3VkLnB5) | `70.9% <100%> (+0.26%)` | :arrow_up: |
| [...zure-cli-vm/azure/cli/command\_modules/vm/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktdm0vYXp1cmUvY2xpL2NvbW1hbmRfbW9kdWxlcy92bS9jdXN0b20ucHk=) | `77.01% <100%> (ø)` | :arrow_up: |
| [src/azure-cli-core/azure/cli/core/util.py](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=tree#diff-c3JjL2F6dXJlLWNsaS1jb3JlL2F6dXJlL2NsaS9jb3JlL3V0aWwucHk=) | `70.06% <0%> (ø)` | :arrow_up: |
| [...dback/azure/cli/command\_modules/feedback/custom.py](https://codecov.io/gh/Azure/azure-cli/pull/3754?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/3754?src=pr&el=tree#diff-c3JjL2NvbW1hbmRfbW9kdWxlcy9henVyZS1jbGktY29tcG9uZW50L2F6dXJlL2NsaS9jb21tYW5kX21vZHVsZXMvY29tcG9uZW50L2N1c3RvbS5weQ==) | `16.23% <0%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/azure-cli/pull/3754?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/3754?src=pr&el=footer). Last update [dff725f...cf59716](https://codecov.io/gh/Azure/azure-cli/pull/3754?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/azure-cli-core/azure/cli/core/cloud.py b/src/azure-cli-core/azure/cli/core/cloud.py
index 5fb720abd..21eefe782 100644
--- a/src/azure-cli-core/azure/cli/core/cloud.py
+++ b/src/azure-cli-core/azure/cli/core/cloud.py
@@ -58,7 +58,8 @@ class CloudEndpoints(object):  # pylint: disable=too-few-public-methods,too-many
                  gallery=None,
                  active_directory=None,
                  active_directory_resource_id=None,
-                 active_directory_graph_resource_id=None):
+                 active_directory_graph_resource_id=None,
+                 vm_image_alias_doc=None):
         # Attribute names are significant. They are used when storing/retrieving clouds from config
         self.management = management
         self.resource_manager = resource_manager
@@ -68,6 +69,7 @@ class CloudEndpoints(object):  # pylint: disable=too-few-public-methods,too-many
         self.active_directory = active_directory
         self.active_directory_resource_id = active_directory_resource_id
         self.active_directory_graph_resource_id = active_directory_graph_resource_id
+        self.vm_image_alias_doc = vm_image_alias_doc
 
     def has_endpoint_set(self, endpoint_name):
         try:
@@ -146,7 +148,8 @@ AZURE_PUBLIC_CLOUD = Cloud(
         gallery='https://gallery.azure.com/',
         active_directory='https://login.microsoftonline.com',
         active_directory_resource_id='https://management.core.windows.net/',
-        active_directory_graph_resource_id='https://graph.windows.net/'),
+        active_directory_graph_resource_id='https://graph.windows.net/',
+        vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json'),  # pylint: disable=line-too-long
     suffixes=CloudSuffixes(
         storage_endpoint='core.windows.net',
         keyvault_dns='.vault.azure.net',
@@ -164,7 +167,8 @@ AZURE_CHINA_CLOUD = Cloud(
         gallery='https://gallery.chinacloudapi.cn/',
         active_directory='https://login.chinacloudapi.cn',
         active_directory_resource_id='https://management.core.chinacloudapi.cn/',
-        active_directory_graph_resource_id='https://graph.chinacloudapi.cn/'),
+        active_directory_graph_resource_id='https://graph.chinacloudapi.cn/',
+        vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json'),  # pylint: disable=line-too-long
     suffixes=CloudSuffixes(
         storage_endpoint='core.chinacloudapi.cn',
         keyvault_dns='.vault.azure.cn',
@@ -180,7 +184,8 @@ AZURE_US_GOV_CLOUD = Cloud(
         gallery='https://gallery.usgovcloudapi.net/',
         active_directory='https://login.microsoftonline.com',
         active_directory_resource_id='https://management.core.usgovcloudapi.net/',
-        active_directory_graph_resource_id='https://graph.windows.net/'),
+        active_directory_graph_resource_id='https://graph.windows.net/',
+        vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json'),   # pylint: disable=line-too-long
     suffixes=CloudSuffixes(
         storage_endpoint='core.usgovcloudapi.net',
         keyvault_dns='.vault.usgovcloudapi.net',
@@ -196,7 +201,8 @@ AZURE_GERMAN_CLOUD = Cloud(
         gallery='https://gallery.cloudapi.de/',
         active_directory='https://login.microsoftonline.de',
         active_directory_resource_id='https://management.core.cloudapi.de/',
-        active_directory_graph_resource_id='https://graph.cloudapi.de/'),
+        active_directory_graph_resource_id='https://graph.cloudapi.de/',
+        vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json'),  # pylint: disable=line-too-long
     suffixes=CloudSuffixes(
         storage_endpoint='core.cloudapi.de',
         keyvault_dns='.vault.microsoftazure.de',
diff --git a/src/command_modules/azure-cli-cloud/HISTORY.rst b/src/command_modules/azure-cli-cloud/HISTORY.rst
index d432f8da5..cd010407e 100644
--- a/src/command_modules/azure-cli-cloud/HISTORY.rst
+++ b/src/command_modules/azure-cli-cloud/HISTORY.rst
@@ -2,10 +2,10 @@
 
 Release History
 ===============
-
 2.0.5 (unreleased)
 ++++++++++++++++++
 * Provide an option for 'az cloud set' to select the profile while selecting current cloud
+* Expose 'endpoint_vm_image_alias_doc'
 
 2.0.4 (2017-06-13)
 ++++++++++++++++++
diff --git a/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/_params.py b/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/_params.py
index ffd0d6f01..ff67ef353 100644
--- a/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/_params.py
+++ b/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/_params.py
@@ -60,6 +60,8 @@ register_cli_argument('cloud', 'endpoint_active_directory_resource_id',
                       help='The resource ID to obtain AD tokens for')
 register_cli_argument('cloud', 'endpoint_active_directory_graph_resource_id',
                       help='The Active Directory resource ID')
+register_cli_argument('cloud', 'endpoint_vm_image_alias_doc',
+                      help='The uri of the document which caches commonly used virtual machine images')
 register_cli_argument('cloud', 'suffix_sql_server_hostname',
                       help='The dns suffix for sql servers')
 register_cli_argument('cloud', 'suffix_storage_endpoint',
diff --git a/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/custom.py b/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/custom.py
index 33b366154..be4732918 100644
--- a/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/custom.py
+++ b/src/command_modules/azure-cli-cloud/azure/cli/command_modules/cloud/custom.py
@@ -58,6 +58,7 @@ def register_cloud(cloud_name,
                    endpoint_active_directory=None,
                    endpoint_active_directory_resource_id=None,
                    endpoint_active_directory_graph_resource_id=None,
+                   endpoint_vm_image_alias_doc=None,
                    suffix_sql_server_hostname=None,
                    suffix_storage_endpoint=None,
                    suffix_keyvault_dns=None,
@@ -81,6 +82,7 @@ def modify_cloud(cloud_name=None,
                  endpoint_active_directory=None,
                  endpoint_active_directory_resource_id=None,
                  endpoint_active_directory_graph_resource_id=None,
+                 endpoint_vm_image_alias_doc=None,
                  suffix_sql_server_hostname=None,
                  suffix_storage_endpoint=None,
                  suffix_keyvault_dns=None,
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_actions.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_actions.py
index cf259d63b..623039c5b 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_actions.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_actions.py
@@ -72,8 +72,13 @@ def load_images_thru_services(publisher, offer, sku, location):
 
 
 def load_images_from_aliases_doc(publisher=None, offer=None, sku=None):
-    target_url = ('https://raw.githubusercontent.com/Azure/azure-rest-api-specs/'
-                  'master/arm-compute/quickstart-templates/aliases.json')
+    from azure.cli.core.cloud import get_active_cloud, CloudEndpointNotSetException
+    cloud = get_active_cloud()
+    try:
+        target_url = cloud.endpoints.vm_image_alias_doc
+    except CloudEndpointNotSetException:
+        raise CLIError("'endpoint_vm_image_alias_doc' isn't configured. Please invoke 'az cloud update' to configure "
+                       "it or use '--all' to retrieve images from server")
     txt = urlopen(target_url).read()
     dic = json.loads(txt.decode())
     try:
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 2833b3512..a3180d0c9 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
@@ -213,9 +213,9 @@ def list_vm_images(image_location=None, publisher_name=None, offer=None, sku=Non
                            "is supported.")
         all_images = load_images_thru_services(publisher_name, offer, sku, image_location)
     else:
+        all_images = load_images_from_aliases_doc(publisher_name, offer, sku)
         logger.warning(
             'You are viewing an offline list of images, use --all to retrieve an up-to-date list')
-        all_images = load_images_from_aliases_doc(publisher_name, offer, sku)
 
     for i in all_images:
         i['urn'] = ':'.join([i['publisher'], i['offer'], i['sku'], i['version']])


Test patch:
diff --git a/src/command_modules/azure-cli-vm/tests/test_vm_image.py b/src/command_modules/azure-cli-vm/tests/test_vm_image.py
index f3b661c14..9c2eb1f0d 100644
--- a/src/command_modules/azure-cli-vm/tests/test_vm_image.py
+++ b/src/command_modules/azure-cli-vm/tests/test_vm_image.py
@@ -8,6 +8,8 @@ import unittest
 import mock
 
 import azure.cli.core.application as application
+from azure.cli.core.util import CLIError
+from azure.cli.core.cloud import CloudEndpointNotSetException
 
 
 class TestVMImage(unittest.TestCase):
@@ -42,6 +44,17 @@ class TestVMImage(unittest.TestCase):
         self.assertEqual(parts[2], ubuntu_image['sku'])
         self.assertEqual(parts[3], ubuntu_image['version'])
 
+    @mock.patch('azure.cli.core.cloud.get_active_cloud', autospec=True)
+    def test_when_alias_doc_is_missing(self, mock_get_active_cloud):
+        from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc
+        p = mock.PropertyMock(side_effect=CloudEndpointNotSetException)
+        mock_cloud = mock.MagicMock()
+        type(mock_cloud.endpoints).vm_image_alias_doc = p
+        mock_get_active_cloud.return_value = mock_cloud
+        # assert
+        with self.assertRaises(CLIError):
+            load_images_from_aliases_doc()
+
 
 if __name__ == '__main__':
     unittest.main()