Repository: Azure/azure-cli

Issue:
`az vm extension set` crashes
```
az vm extension set -n IaaSAntimalware --publisher Microsoft.Azure.Security -g TestDesktop_RG --vm-name vmAWB10Test09

Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/azure/cli/main.py", line 37, in main
    cmd_result = APPLICATION.execute(args)
  File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/azure/cli/core/application.py", line 157, in execute
    result = expanded_arg.func(params)
  File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/azure/cli/core/commands/__init__.py", line 358, in _execute_command
    raise ex
  File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/azure/cli/core/commands/__init__.py", line 353, in _execute_command
    result = op(client, **kwargs) if client else op(**kwargs)
  File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/azure/cli/command_modules/vm/custom.py", line 861, in set_extension
    instance_name = _get_extension_instance_name(vm.instance_view, publisher, vm_extension_name)
  File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/azure/cli/command_modules/vm/custom.py", line 712, in _get_extension_instance_name
    ext = next((x for x in instance_view.extensions
StopIteration
```

Discussion:

Patch:
diff --git a/azure-cli.pyproj b/azure-cli.pyproj
index 40fc0e2dc..1b3a00234 100644
--- a/azure-cli.pyproj
+++ b/azure-cli.pyproj
@@ -587,6 +587,8 @@
     <Folder Include="command_modules\azure-cli-network\azure\cli\command_modules\network\mgmt_app_gateway\lib\" />
     <Folder Include="command_modules\azure-cli-network\azure\cli\command_modules\network\mgmt_app_gateway\lib\models\" />
     <Folder Include="command_modules\azure-cli-network\azure\cli\command_modules\network\mgmt_app_gateway\lib\operations\" />
+    <Folder Include="command_modules\azure-cli-network\azure\cli\command_modules\network\tests\" />
+    <Folder Include="command_modules\azure-cli-network\azure\cli\command_modules\network\tests\zone_files\" />
     <Folder Include="command_modules\azure-cli-network\azure\cli\command_modules\network\zone_file\" />
     <Folder Include="command_modules\azure-cli-network\tests\" />
     <Folder Include="command_modules\azure-cli-network\tests\zone_files\" />
@@ -596,6 +598,7 @@
     <Folder Include="command_modules\azure-cli-redis\azure\cli\command_modules\" />
     <Folder Include="command_modules\azure-cli-redis\azure\cli\command_modules\redis\" />
     <Folder Include="command_modules\azure-cli-redis\tests\" />
+    <Folder Include="command_modules\azure-cli-resource\azure\cli\command_modules\resource\tests\" />
     <Folder Include="command_modules\azure-cli-resource\tests\" />
     <Folder Include="command_modules\azure-cli-role\tests\" />
     <Folder Include="command_modules\azure-cli-redis\azure\cli\command_modules\redis\tests\" />
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_params.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_params.py
index fb0d3ea18..c76f2c1ff 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_params.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_params.py
@@ -208,7 +208,7 @@ for scope in ['vm create', 'vmss create']:
     register_cli_argument(scope, 'use_unmanaged_disk', action='store_true', help='Do not use managed disk to persist VM', arg_group='Storage')
     register_cli_argument(scope, 'data_disk_sizes_gb', nargs='+', type=int, help='space separated empty managed data disk sizes in GB to create', arg_group='Storage')
     register_cli_argument(scope, 'image_data_disks', ignore_type)
-    for item in ['storage_account', 'public_ip', 'nsg', 'nic', 'vnet', 'load_balancer']:
+    for item in ['storage_account', 'public_ip', 'nsg', 'nic', 'vnet', 'load_balancer', 'app_gateway']:
         register_cli_argument(scope, '{}_type'.format(item), ignore_type)
 
     register_cli_argument(scope, 'vnet_name', help='Name of the virtual network when creating a new one or referencing an existing one.', arg_group='Network')
@@ -231,10 +231,12 @@ register_cli_argument('vm create', 'attach_os_disk', help='Attach an existing OS
 register_cli_argument('vm create', 'availability_set', help='Name or ID of an existing availability set to add the VM to. None by default.')
 
 register_cli_argument('vmss create', 'vmss_name', name_arg_type, id_part=None, help='Name of the virtual machine scale set.', validator=process_vmss_create_namespace)
-register_cli_argument('vmss create', 'load_balancer', help='Name to use when creating a new load balancer (default) or referencing an existing one. Can also reference an existing load balancer by ID or specify "" for none.', arg_group='Load Balancer')
-register_cli_argument('vmss create', 'backend_pool_name', help='Name to use for the backend pool when creating a new load balancer.', arg_group='Load Balancer')
-register_cli_argument('vmss create', 'nat_pool_name', help='Name to use for the NAT pool when creating a new load balancer.', arg_group='Load Balancer')
-register_cli_argument('vmss create', 'backend_port', help='Backend port to open with NAT rules. Defaults to 22 on Linux and 3389 on Windows.', type=int, arg_group='Load Balancer')
+register_cli_argument('vmss create', 'load_balancer', help='Name to use when creating a new load balancer (default) or referencing an existing one. Can also reference an existing load balancer by ID or specify "" for none.', options_list=['--load-balancer', '--lb'], arg_group='Network Balancer')
+register_cli_argument('vmss create', 'application_gateway', help='Name to use when creating a new application gateway (default) or referencing an existing one. Can also reference an existing application gateway by ID or specify "" for none.', options_list=['--app-gateway'], arg_group='Network Balancer')
+register_cli_argument('vmss create', 'backend_pool_name', help='Name to use for the backend pool when creating a new load balancer or application gateway.', arg_group='Network Balancer')
+register_cli_argument('vmss create', 'nat_pool_name', help='Name to use for the NAT pool when creating a new load balancer.', options_list=['--lb-nat-pool-name', '--nat-pool-name'], arg_group='Network Balancer')
+register_cli_argument('vmss create', 'backend_port', help='When creating a new load balancer, backend port to open with NAT rules (Defaults to 22 on Linux and 3389 on Windows). When creating an application gateway, the backend port to use for the backend HTTP settings.', type=int, arg_group='Network Balancer')
+register_cli_argument('vmss create', 'app_gateway_subnet_address_prefix', help='The subnet IP address prefix to use when creating a new application gateway in CIDR format.', arg_group='Network Balancer')
 register_cli_argument('vmss create', 'instance_count', help='Number of VMs in the scale set.', type=int)
 register_cli_argument('vmss create', 'disable_overprovision', help='Overprovision option (see https://azure.microsoft.com/en-us/documentation/articles/virtual-machine-scale-sets-overview/ for details).', action='store_true')
 register_cli_argument('vmss create', 'upgrade_policy_mode', help=None, **enum_choice_list(UpgradeMode))
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_template_builder.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_template_builder.py
index d0440e807..5ec47d161 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_template_builder.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_template_builder.py
@@ -411,13 +411,10 @@ def _build_data_disks(profile, data_disk_sizes_gb, image_data_disks,
     return profile
 
 
-def build_load_balancer_resource(name, location, tags, backend_pool_name, nat_pool_name,
-                                 backend_port, frontend_ip_name, public_ip_id, subnet_id,
-                                 private_ip_address='', private_ip_allocation='dynamic'):
-    lb_id = "resourceId('Microsoft.Network/loadBalancers', '{}')".format(name)
-
+def _build_frontend_ip_config(name, public_ip_id=None, private_ip_allocation=None,
+                              private_ip_address=None, subnet_id=None):
     frontend_ip_config = {
-        'name': frontend_ip_name
+        'name': name
     }
 
     if public_ip_id:
@@ -438,6 +435,109 @@ def build_load_balancer_resource(name, location, tags, backend_pool_name, nat_po
                 }
             }
         })
+    return frontend_ip_config
+
+
+def build_application_gateway_resource(name, location, tags, backend_pool_name, backend_port,
+                                       frontend_ip_name, public_ip_id,
+                                       subnet_id, gateway_subnet_id,
+                                       private_ip_address, private_ip_allocation):
+    frontend_ip_config = _build_frontend_ip_config(frontend_ip_name, public_ip_id,
+                                                   private_ip_address, private_ip_allocation,
+                                                   subnet_id)
+
+    def _ag_subresource_id(_type, name):
+        return "[concat(variables('appGwID'), '/{}/{}')]".format(_type, name)
+
+    frontend_ip_config_id = _ag_subresource_id('frontendIPConfigurations', frontend_ip_name)
+    frontend_port_id = _ag_subresource_id('frontendPorts', 'appGwFrontendPort')
+    http_listener_id = _ag_subresource_id('httpListeners', 'appGwHttpListener')
+    backend_address_pool_id = _ag_subresource_id('backendAddressPools', backend_pool_name)
+    backend_http_settings_id = _ag_subresource_id(
+        'backendHttpSettingsCollection', 'appGwBackendHttpSettings')
+
+    ag_properties = {
+        'backendAddressPools': [
+            {
+                'name': backend_pool_name
+            }
+        ],
+        'backendHttpSettingsCollection': [
+            {
+                'name': 'appGwBackendHttpSettings',
+                'properties': {
+                    'Port': backend_port,
+                    'Protocol': 'Http',
+                    'CookieBasedAffinity': 'Disabled'
+                }
+            }
+        ],
+        'frontendIPConfigurations': [frontend_ip_config],
+        'frontendPorts': [
+            {
+                'name': 'appGwFrontendPort',
+                'properties': {
+                    'Port': 80
+                }
+            }
+        ],
+        'gatewayIPConfigurations': [
+            {
+                'name': 'appGwIpConfig',
+                'properties': {
+                    'subnet': {'id': gateway_subnet_id}
+                }
+            }
+        ],
+        'httpListeners': [
+            {
+                'name': 'appGwHttpListener',
+                'properties': {
+                    'FrontendIPConfiguration': {'Id': frontend_ip_config_id},
+                    'FrontendPort': {'Id': frontend_port_id},
+                    'Protocol': 'Http',
+                    'SslCertificate': None
+                }
+            }
+        ],
+        'sku': {
+            'name': 'Standard_Large',
+            'tier': 'Standard',
+            'capacity': '10'
+        },
+        'requestRoutingRules': [
+            {
+                'Name': 'rule1',
+                'properties': {
+                    'RuleType': 'Basic',
+                    'httpListener': {'id': http_listener_id},
+                    'backendAddressPool': {'id': backend_address_pool_id},
+                    'backendHttpSettings': {'id': backend_http_settings_id}
+                }
+            }
+        ]
+    }
+
+    ag = {
+        'type': 'Microsoft.Network/applicationGateways',
+        'name': name,
+        'location': location,
+        'tags': tags,
+        'apiVersion': '2015-06-15',
+        'dependsOn': [],
+        'properties': ag_properties
+    }
+    return ag
+
+
+def build_load_balancer_resource(name, location, tags, backend_pool_name, nat_pool_name,
+                                 backend_port, frontend_ip_name, public_ip_id, subnet_id,
+                                 private_ip_address, private_ip_allocation):
+    lb_id = "resourceId('Microsoft.Network/loadBalancers', '{}')".format(name)
+
+    frontend_ip_config = _build_frontend_ip_config(frontend_ip_name, public_ip_id,
+                                                   private_ip_address, private_ip_allocation,
+                                                   subnet_id)
 
     lb_properties = {
         'backendAddressPools': [
@@ -464,13 +564,13 @@ def build_load_balancer_resource(name, location, tags, backend_pool_name, nat_po
     }
 
     lb = {
-        "type": "Microsoft.Network/loadBalancers",
-        "name": name,
-        "location": location,
-        "tags": tags,
-        "apiVersion": "2015-06-15",
-        "dependsOn": [],
-        "properties": lb_properties
+        'type': 'Microsoft.Network/loadBalancers',
+        'name': name,
+        'location': location,
+        'tags': tags,
+        'apiVersion': '2015-06-15',
+        'dependsOn': [],
+        'properties': lb_properties
     }
     return lb
 
@@ -513,8 +613,11 @@ def build_vmss_resource(name, naming_prefix, location, tags, overprovision, upgr
             'subnet': {'id': subnet_id}
         }
     }
+
     if backend_address_pool_id:
-        ip_configuration['properties']['loadBalancerBackendAddressPools'] = [
+        key = 'loadBalancerBackendAddressPools' if 'loadBalancers' in backend_address_pool_id \
+            else 'ApplicationGatewayBackendAddressPools'
+        ip_configuration['properties'][key] = [
             {'id': backend_address_pool_id}
         ]
 
diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py
index 2bc5ab655..08009d150 100644
--- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py
+++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py
@@ -199,7 +199,15 @@ storage_profile_param_options = {
 }
 
 
-def _validate_required_forbidden_parameters(namespace, required, forbidden):
+network_balancer_param_options = {
+    'application_gateway': '--application-gateway',
+    'load_balancer': '--load-balancer',
+    'app_gateway_subnet_address_prefix': '--app-gateway-subnet-address-prefix',
+    'backend_pool_name': '--backend-pool-name'
+}
+
+
+def _validate_storage_required_forbidden_parameters(namespace, required, forbidden):
     missing_required = [x for x in required if not getattr(namespace, x)]
     included_forbidden = [x for x in forbidden if getattr(namespace, x)]
     if missing_required or included_forbidden:
@@ -216,6 +224,22 @@ def _validate_required_forbidden_parameters(namespace, required, forbidden):
         raise CLIError(error)
 
 
+def _validate_network_balancer_required_forbidden_parameters(namespace, required, forbidden, desc):
+    missing_required = [x for x in required if not getattr(namespace, x)]
+    included_forbidden = [x for x in forbidden if getattr(namespace, x)]
+    if missing_required or included_forbidden:
+        error = 'invalid usage for network balancer: {}'.format(desc)
+        if missing_required:
+            missing_string = ', '.join(
+                network_balancer_param_options[x] for x in missing_required)
+            error = '{}\n\tmissing: {}'.format(error, missing_string)
+        if included_forbidden:
+            forbidden_string = ', '.join(
+                network_balancer_param_options[x] for x in included_forbidden)
+            error = '{}\n\tnot applicable: {}'.format(error, forbidden_string)
+        raise CLIError(error)
+
+
 def _validate_managed_disk_sku(sku):
 
     allowed_skus = ['Premium_LRS', 'Standard_LRS']
@@ -309,7 +333,7 @@ def _validate_vm_create_storage_profile(namespace, for_scale_set=False):
         namespace.storage_sku = 'Standard_LRS' if for_scale_set else 'Premium_LRS'
 
     # Now verify that the status of required and forbidden parameters
-    _validate_required_forbidden_parameters(namespace, required, forbidden)
+    _validate_storage_required_forbidden_parameters(namespace, required, forbidden)
 
     if namespace.storage_profile == StorageProfile.ManagedCustomImage:
         # extract additional information from a managed custom image
@@ -452,6 +476,10 @@ def _validate_vmss_create_subnet(namespace):
                 raise CLIError(err.format(namespace.instance_count))
             namespace.subnet_address_prefix = '{}/{}'.format(cidr, i)
 
+        if namespace.app_gateway_type and namespace.app_gateway_subnet_address_prefix is None:
+            raise CLIError('Must specify --gateway-subnet-address-prefix to create an '
+                           'application gateway.')
+
 
 def _validate_vm_create_nsg(namespace):
 
@@ -481,10 +509,10 @@ def _validate_vm_create_public_ip(namespace):
 
 
 def _validate_vmss_create_public_ip(namespace):
-    if namespace.load_balancer_type is None:
+    if namespace.load_balancer_type is None and namespace.app_gateway_type is None:
         if namespace.public_ip_address:
-            raise CLIError('--public-ip-address is not applicable when there is no load-balancer '
-                           'attached, or implictly disabled due to 100+ instance count')
+            raise CLIError('--public-ip-address can only be used  when creating a new load '
+                           'balancer or application gateway frontend.')
         namespace.public_ip_address = ''
     _validate_vm_create_public_ip(namespace)
 
@@ -696,36 +724,90 @@ def process_vm_create_namespace(namespace):
 
 # region VMSS Create Validators
 
+def _get_vmss_create_instance_threshold():
+    return 100
+
+
+def _validate_vmss_create_load_balancer_or_app_gateway(namespace):
+
+    INSTANCE_THRESHOLD = _get_vmss_create_instance_threshold()
 
-def _validate_vmss_create_load_balancer(namespace):
     # convert the single_placement_group to boolean for simpler logic beyond
     if namespace.single_placement_group is None:
-        namespace.single_placement_group = namespace.instance_count <= 100
+        namespace.single_placement_group = namespace.instance_count <= INSTANCE_THRESHOLD
     else:
         namespace.single_placement_group = (namespace.single_placement_group == 'true')
 
-    if not namespace.single_placement_group:
+    if not namespace.single_placement_group and namespace.load_balancer:
+        raise CLIError(
+            '--load-balancer is not applicable when --single-placement-group is turned off.')
+
+    if namespace.load_balancer and namespace.application_gateway:
+        raise CLIError('incorrect usage: --load-balancer NAME_OR_ID | '
+                       '--application-gateway NAME_OR_ID')
+
+    if namespace.instance_count > INSTANCE_THRESHOLD and namespace.load_balancer:
+        raise CLIError(
+            '--load-balancer cannot be used with --instance_count is > {}'.format(
+                INSTANCE_THRESHOLD))
+
+    # Resolve the type of balancer (if any) being used
+    balancer_type = 'None'
+    if namespace.load_balancer is None and namespace.application_gateway is None:
+        # use defaulting rules to determine
+        balancer_type = 'loadBalancer' if namespace.instance_count <= INSTANCE_THRESHOLD \
+            else 'applicationGateway'
+    elif namespace.load_balancer:
+        balancer_type = 'loadBalancer'
+    elif namespace.application_gateway:
+        balancer_type = 'applicationGateway'
+
+    if balancer_type == 'applicationGateway':
+
+        if namespace.application_gateway:
+            if check_existence(namespace.application_gateway, namespace.resource_group_name,
+                               'Microsoft.Network', 'applicationGateways'):
+                namespace.app_gateway_type = 'existing'
+            else:
+                namespace.app_gateway_type = 'new'
+        elif namespace.application_gateway == '':
+            namespace.app_gateway_type = None
+        elif namespace.application_gateway is None:
+            namespace.app_gateway_type = 'new'
+
+        # AppGateway frontend
+        required = []
+        if namespace.app_gateway_type == 'new':
+            required.append('app_gateway_subnet_address_prefix')
+        elif namespace.app_gateway_type == 'existing':
+            required.append('backend_pool_name')
+        forbidden = ['nat_pool_name', 'load_balancer']
+        _validate_network_balancer_required_forbidden_parameters(
+            namespace, required, forbidden, 'application gateway')
+
+    elif balancer_type == 'loadBalancer':
+        # LoadBalancer frontend
+        required = []
+        forbidden = ['app_gateway_subnet_address_prefix', 'application_gateway']
+        _validate_network_balancer_required_forbidden_parameters(
+            namespace, required, forbidden, 'load balancer')
+
         if namespace.load_balancer:
-            raise CLIError('--load-balancer is not applicable when --single-placement-group is '
-                           'explictly turned off or implictly turned off for 100+ instance count')
-        namespace.load_balancer = ''
-
-    if namespace.load_balancer:
-        if check_existence(namespace.load_balancer, namespace.resource_group_name,
-                           'Microsoft.Network', 'loadBalancers'):
-            namespace.load_balancer_type = 'existing'
-        else:
+            if check_existence(namespace.load_balancer, namespace.resource_group_name,
+                               'Microsoft.Network', 'loadBalancers'):
+                namespace.load_balancer_type = 'existing'
+            else:
+                namespace.load_balancer_type = 'new'
+        elif namespace.load_balancer == '':
+            namespace.load_balancer_type = None
+        elif namespace.load_balancer is None:
             namespace.load_balancer_type = 'new'
-    elif namespace.load_balancer == '':
-        namespace.load_balancer_type = None
-    elif namespace.load_balancer is None:
-        namespace.load_balancer_type = 'new'
 
 
 def process_vmss_create_namespace(namespace):
     get_default_location_from_resource_group(namespace)
     _validate_vm_create_storage_profile(namespace, for_scale_set=True)
-    _validate_vmss_create_load_balancer(namespace)
+    _validate_vmss_create_load_balancer_or_app_gateway(namespace)
     _validate_vm_create_vnet(namespace, for_scale_set=True)
     _validate_vmss_create_subnet(namespace)
     _validate_vmss_create_public_ip(namespace)
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 04e1d09f2..2c1df1830 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
@@ -725,7 +725,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()))
+                    if x.type.lower() == full_type_name.lower()), None)
         if ext:
             extension_instance_name = ext.name
     return extension_instance_name
@@ -1442,7 +1442,7 @@ def list_vmss_instance_connection_info(resource_group_name, vm_scale_set_name):
                                                  vm_scale_set_name)
     # find the load balancer
     nic_configs = vmss.virtual_machine_profile.network_profile.network_interface_configurations
-    primary_nic_config = next(n for n in nic_configs if n.primary)
+    primary_nic_config = next((n for n in nic_configs if n.primary), None)
     if primary_nic_config is None:
         raise CLIError('could not find a primary nic which is needed to search to load balancer')
     ip_configs = primary_nic_config.ip_configurations
@@ -1659,7 +1659,9 @@ def create_vmss(vmss_name, resource_group_name, image,
                 admin_username=getpass.getuser(), admin_password=None, authentication_type=None,
                 vm_sku="Standard_D1_v2", no_wait=False,
                 ssh_dest_key_path=None, ssh_key_value=None, generate_ssh_keys=False,
-                load_balancer=None, backend_pool_name=None, nat_pool_name=None, backend_port=None,
+                load_balancer=None, application_gateway=None,
+                app_gateway_subnet_address_prefix=None,
+                backend_pool_name=None, nat_pool_name=None, backend_port=None,
                 public_ip_address=None, public_ip_address_allocation='dynamic',
                 public_ip_address_dns_name=None,
                 os_caching=None, data_caching=None,
@@ -1669,7 +1671,8 @@ def create_vmss(vmss_name, resource_group_name, image,
                 vnet_name=None, vnet_address_prefix='10.0.0.0/16',
                 subnet=None, subnet_address_prefix=None,
                 os_offer=None, os_publisher=None, os_sku=None, os_version=None,
-                load_balancer_type=None, vnet_type=None, public_ip_type=None, storage_profile=None,
+                load_balancer_type=None, app_gateway_type=None, vnet_type=None,
+                public_ip_type=None, storage_profile=None,
                 single_placement_group=None, custom_data=None, secrets=None):
     from azure.cli.core.commands.client_factory import get_subscription_id
     from azure.cli.core._util import random_string
@@ -1677,7 +1680,7 @@ def create_vmss(vmss_name, resource_group_name, image,
         ArmTemplateBuilder, StorageProfile, build_vmss_resource, build_storage_account_resource,
         build_vnet_resource, build_public_ip_resource, build_load_balancer_resource,
         build_output_deployment_resource, build_deployment_resource,
-        build_vmss_storage_account_pool_resource)
+        build_vmss_storage_account_pool_resource, build_application_gateway_resource)
 
     from azure.cli.core._profile import CLOUD
     from azure.mgmt.resource.resources import ResourceManagementClient
@@ -1688,6 +1691,10 @@ def create_vmss(vmss_name, resource_group_name, image,
         subscription=get_subscription_id(), resource_group=resource_group_name,
         namespace='Microsoft.Network')
 
+    scrubbed_name = vmss_name.replace('-', '').lower()[:5]
+    naming_prefix = '{}{}'.format(scrubbed_name,
+                                  random_string(9 - len(scrubbed_name), force_lower=True))
+
     # determine final defaults and calculated values
     tags = tags or {}
     os_disk_name = os_disk_name or 'osdisk_{}'.format(random_string(10))
@@ -1698,16 +1705,33 @@ def create_vmss(vmss_name, resource_group_name, image,
     master_template = ArmTemplateBuilder()
 
     vmss_dependencies = []
+
+    # VNET will always be a dependency
     if vnet_type == 'new':
         vnet_name = vnet_name or '{}VNET'.format(vmss_name)
         subnet = subnet or '{}Subnet'.format(vmss_name)
         vmss_dependencies.append('Microsoft.Network/virtualNetworks/{}'.format(vnet_name))
-        master_template.add_resource(build_vnet_resource(
-            vnet_name, location, tags, vnet_address_prefix, subnet, subnet_address_prefix))
+        vnet = build_vnet_resource(
+            vnet_name, location, tags, vnet_address_prefix, subnet, subnet_address_prefix)
+        if app_gateway_type:
+            vnet['properties']['subnets'].append({
+                'name': 'appGwSubnet',
+                'properties': {
+                    'addressPrefix': app_gateway_subnet_address_prefix
+                }
+            })
+        master_template.add_resource(vnet)
 
     subnet_id = subnet if is_valid_resource_id(subnet) else \
         '{}/virtualNetworks/{}/subnets/{}'.format(network_id_template, vnet_name, subnet)
+    gateway_subnet_id = \
+        '{}/virtualNetworks/{}/subnets/appGwSubnet'.format(network_id_template, vnet_name) \
+        if app_gateway_type == 'new' else None
+
+    # public IP is used by either load balancer/application gateway
+    public_ip_address_id = public_ip_address if is_valid_resource_id(public_ip_address) else None
 
+    # Handle load balancer creation
     if load_balancer_type == 'new':
         load_balancer = load_balancer or '{}LB'.format(vmss_name)
         vmss_dependencies.append('Microsoft.Network/loadBalancers/{}'.format(load_balancer))
@@ -1720,10 +1744,8 @@ def create_vmss(vmss_name, resource_group_name, image,
                                                                   tags,
                                                                   public_ip_address_allocation,
                                                                   public_ip_address_dns_name))
-        public_ip_address_id = None
-        if public_ip_address:
-            public_ip_address_id = public_ip_address if is_valid_resource_id(public_ip_address) \
-                else '{}/publicIPAddresses/{}'.format(network_id_template, public_ip_address)
+            public_ip_address_id = '{}/publicIPAddresses/{}'.format(network_id_template,
+                                                                    public_ip_address)
 
         # calculate default names if not provided
         backend_pool_name = backend_pool_name or '{}BEPool'.format(load_balancer)
@@ -1733,14 +1755,43 @@ def create_vmss(vmss_name, resource_group_name, image,
 
         lb_resource = build_load_balancer_resource(
             load_balancer, location, tags, backend_pool_name, nat_pool_name, backend_port,
-            'loadBalancerFrontEnd', public_ip_address_id, subnet_id)
+            'loadBalancerFrontEnd', public_ip_address_id, subnet_id,
+            private_ip_address='', private_ip_allocation='Dynamic')
         lb_resource['dependsOn'] = lb_dependencies
         master_template.add_resource(lb_resource)
 
-    scrubbed_name = vmss_name.replace('-', '').lower()[:5]
-    naming_prefix = '{}{}'.format(scrubbed_name,
-                                  random_string(9 - len(scrubbed_name), force_lower=True))
+    # Or handle application gateway creation
+    app_gateway = application_gateway
+    if app_gateway_type == 'new':
+        app_gateway = application_gateway or '{}AG'.format(vmss_name)
+        vmss_dependencies.append('Microsoft.Network/applicationGateways/{}'.format(app_gateway))
+
+        ag_dependencies = []
+        if public_ip_type == 'new':
+            public_ip_address = public_ip_address or '{}PublicIP'.format(app_gateway)
+            ag_dependencies.append('Microsoft.Network/publicIpAddresses/{}'.format(public_ip_address))  # pylint: disable=line-too-long
+            master_template.add_resource(build_public_ip_resource(public_ip_address, location,
+                                                                  tags,
+                                                                  public_ip_address_allocation,
+                                                                  public_ip_address_dns_name))
+            public_ip_address_id = '{}/publicIPAddresses/{}'.format(network_id_template,
+                                                                    public_ip_address)
 
+        # calculate default names if not provided
+        backend_pool_name = backend_pool_name or '{}BEPool'.format(app_gateway)
+        backend_port = backend_port or 80
+
+        ag_resource = build_application_gateway_resource(
+            app_gateway, location, tags, backend_pool_name, backend_port, 'appGwFrontendIP',
+            public_ip_address_id, subnet_id, gateway_subnet_id, private_ip_address='',
+            private_ip_allocation='Dynamic')
+        ag_resource['dependsOn'] = ag_dependencies
+        master_template.add_variable(
+            'appGwID',
+            "[resourceId('Microsoft.Network/applicationGateways', '{}')]".format(app_gateway))
+        master_template.add_resource(ag_resource)
+
+    # create storage accounts if needed for unmanaged disk storage
     if storage_profile in [StorageProfile.SACustomImage, StorageProfile.SAPirImage]:
         master_template.add_resource(build_vmss_storage_account_pool_resource(
             'storageLoop', location, tags, storage_sku))
@@ -1755,18 +1806,27 @@ def create_vmss(vmss_name, resource_group_name, image,
 
     backend_address_pool_id = None
     inbound_nat_pool_id = None
-    if is_valid_resource_id(load_balancer):
-        backend_address_pool_id = \
-            '{}/backendAddressPools/{}'.format(load_balancer, backend_pool_name) \
-            if load_balancer_type else None
-        inbound_nat_pool_id = '{}/inboundNatPools/{}'.format(load_balancer, nat_pool_name) \
-            if load_balancer_type == 'new' else None
-    else:
-        backend_address_pool_id = '{}/loadBalancers/{}/backendAddressPools/{}'.format(
-            network_id_template, load_balancer, backend_pool_name) if load_balancer_type else None
-        inbound_nat_pool_id = '{}/loadBalancers/{}/inboundNatPools/{}'.format(
-            network_id_template, load_balancer, nat_pool_name) if load_balancer_type == 'new' \
-            else None
+    if load_balancer_type or app_gateway_type:
+        network_balancer = load_balancer or app_gateway
+        balancer_type = 'loadBalancers' if load_balancer_type else 'applicationGateways'
+
+        if is_valid_resource_id(network_balancer):
+            # backend address pool needed by load balancer or app gateway
+            backend_address_pool_id = \
+                '{}/backendAddressPools/{}'.format(network_balancer, backend_pool_name)
+
+            # nat pool only applies to new load balancers
+            inbound_nat_pool_id = '{}/inboundNatPools/{}'.format(load_balancer, nat_pool_name) \
+                if load_balancer_type == 'new' else None
+        else:
+            # backend address pool needed by load balancer or app gateway
+            backend_address_pool_id = '{}/{}/{}/backendAddressPools/{}'.format(
+                network_id_template, balancer_type, network_balancer, backend_pool_name)
+
+            # nat pool only applies to new load balancers
+            inbound_nat_pool_id = '{}/loadBalancers/{}/inboundNatPools/{}'.format(
+                network_id_template, load_balancer, nat_pool_name) if load_balancer_type == 'new' \
+                else None
 
     ip_config_name = '{}IPConfig'.format(naming_prefix)
     nic_name = '{}Nic'.format(naming_prefix)


Test patch:
diff --git a/src/command_modules/azure-cli-vm/tests/test_custom_vm_commands.py b/src/command_modules/azure-cli-vm/tests/test_custom_vm_commands.py
index 7ea9fe5ab..29b1c0205 100644
--- a/src/command_modules/azure-cli-vm/tests/test_custom_vm_commands.py
+++ b/src/command_modules/azure-cli-vm/tests/test_custom_vm_commands.py
@@ -12,7 +12,8 @@ from azure.cli.command_modules.vm.custom import enable_boot_diagnostics, disable
     _merge_secrets
 from azure.cli.command_modules.vm.custom import (_get_access_extension_upgrade_info,
                                                  _LINUX_ACCESS_EXT,
-                                                 _WINDOWS_ACCESS_EXT)
+                                                 _WINDOWS_ACCESS_EXT,
+                                                 _get_extension_instance_name)
 from azure.cli.command_modules.vm.custom import \
     (attach_unmanaged_data_disk, detach_data_disk, get_vmss_instance_view)
 from azure.cli.command_modules.vm.disk_encryption import enable, disable, _check_encrypt_is_supported
@@ -342,6 +343,18 @@ class Test_Vm_Custom(unittest.TestCase):
         vault123Expected.sort(key=lambda x: x['certificateUrl'])
         self.assertListEqual(vault123Expected, vault123)
 
+    def test_get_extension_instance_name(self):
+        instance_view = mock.MagicMock()
+        extension = mock.MagicMock()
+        extension.type = 'publisher2.extension2'
+        instance_view.extensions = [extension]
+
+        # action
+        result = _get_extension_instance_name(instance_view, 'publisher1', 'extension1')
+
+        # assert
+        self.assertEqual(result, 'extension1')
+
 
 class FakedVM:  # pylint: disable=too-few-public-methods,old-style-class
     def __init__(self, nics=None, disks=None, os_disk=None):
diff --git a/src/command_modules/azure-cli-vm/tests/test_vm_defaults.py b/src/command_modules/azure-cli-vm/tests/test_vm_defaults.py
index 9543ccd5f..4ebb92bb8 100644
--- a/src/command_modules/azure-cli-vm/tests/test_vm_defaults.py
+++ b/src/command_modules/azure-cli-vm/tests/test_vm_defaults.py
@@ -175,6 +175,8 @@ class TestVMSSCreateDefaultVnet(unittest.TestCase):
         ns.vnet_type = 'new'
         ns.vnet_address_prefix = '10.0.0.0/16'
         ns.subnet_address_prefix = None
+        ns.app_gateway_type = 'new'
+        ns.app_gateway_subnet_address_prefix = '10.0.1.0/22'
         ns.instance_count = 1000
         _validate_vmss_create_subnet(ns)
         self.assertEqual('10.0.0.0/22', ns.subnet_address_prefix)
@@ -184,6 +186,8 @@ class TestVMSSCreateDefaultVnet(unittest.TestCase):
         ns.vnet_type = 'new'
         ns.vnet_address_prefix = '10.0.0.0/16'
         ns.subnet_address_prefix = None
+        ns.app_gateway_type = None
+        ns.app_gateway_subnet_address_prefix = None
         ns.instance_count = 2
         _validate_vmss_create_subnet(ns)
         self.assertEqual('10.0.0.0/24', ns.subnet_address_prefix)