diff --git a/azure-cli2017.pyproj b/azure-cli2017.pyproj
index f1e09e0e224..afd37e34312 100644
--- a/azure-cli2017.pyproj
+++ b/azure-cli2017.pyproj
@@ -256,6 +256,12 @@
+
+ Code
+
+
+ Code
+
diff --git a/src/command_modules/azure-cli-appservice/HISTORY.rst b/src/command_modules/azure-cli-appservice/HISTORY.rst
index 1cdf63b481e..43e5e50ef64 100644
--- a/src/command_modules/azure-cli-appservice/HISTORY.rst
+++ b/src/command_modules/azure-cli-appservice/HISTORY.rst
@@ -4,8 +4,10 @@ Release History
===============
0.2.8
+++++
+* webapp: adding support for az webapp up command (Preview) that helps in creating & deploying contents to app
* webapp: fix a bug on container based windows app due to backend change
+
0.2.7
+++++
* webapp, functionapp: Zip deployment default timeout to poll for the status increased to 5 mins, also adding a timeout property to customize this value
diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py
new file mode 100644
index 00000000000..cbc81fb2745
--- /dev/null
+++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py
@@ -0,0 +1,18 @@
+# --------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+# --------------------------------------------------------------------------------------------
+
+NODE_VERSION_DEFAULT = "8.11"
+NETCORE_VERSION_DEFAULT = "2.0"
+DOTNET_VERSION_DEFAULT = "4.7"
+PYTHON_VERSION_DEFAULT = "3.7"
+NETCORE_RUNTIME_NAME = "dotnetcore"
+DOTNET_RUNTIME_NAME = "aspnet"
+NODE_RUNTIME_NAME = "node"
+PYTHON_RUNTIME_NAME = "python"
+OS_DEFAULT = "Windows"
+STATIC_RUNTIME_NAME = "static" # not an oficial supported runtime but used for CLI logic
+NODE_VERSIONS = ['4.4', '4.5', '6.2', '6.6', '6.9', '6.11', '8.0', '8.1', '8.9', '8.11']
+NETCORE_VERSIONS = ['1.0', '1.1', '2.0']
+DOTNET_VERSIONS = ['3.5', '4.7']
diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py
new file mode 100644
index 00000000000..f0563ddc7a9
--- /dev/null
+++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py
@@ -0,0 +1,268 @@
+
+# --------------------------------------------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License. See License.txt in the project root for license information.
+# --------------------------------------------------------------------------------------------
+
+import os
+import zipfile
+from azure.cli.core.commands.client_factory import get_mgmt_service_client
+from azure.mgmt.resource.resources.models import ResourceGroup
+from ._constants import (NETCORE_VERSION_DEFAULT, NETCORE_VERSIONS, NODE_VERSION_DEFAULT,
+ NODE_VERSIONS, NETCORE_RUNTIME_NAME, NODE_RUNTIME_NAME, DOTNET_RUNTIME_NAME,
+ DOTNET_VERSION_DEFAULT, DOTNET_VERSIONS, STATIC_RUNTIME_NAME,
+ PYTHON_RUNTIME_NAME, PYTHON_VERSION_DEFAULT)
+
+
+def _resource_client_factory(cli_ctx, **_):
+ from azure.cli.core.profiles import ResourceType
+ return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES)
+
+
+def web_client_factory(cli_ctx, **_):
+ from azure.mgmt.web import WebSiteManagementClient
+ return get_mgmt_service_client(cli_ctx, WebSiteManagementClient)
+
+
+def zip_contents_from_dir(dirPath, lang):
+ relroot = os.path.abspath(os.path.join(dirPath, os.pardir))
+ path_and_file = os.path.splitdrive(dirPath)[1]
+ file_val = os.path.split(path_and_file)[1]
+ zip_file_path = relroot + os.path.sep + file_val + ".zip"
+ abs_src = os.path.abspath(dirPath)
+ with zipfile.ZipFile("{}".format(zip_file_path), "w", zipfile.ZIP_DEFLATED) as zf:
+ for dirname, subdirs, files in os.walk(dirPath):
+ # skip node_modules folder for Node apps,
+ # since zip_deployment will perfom the build operation
+ if lang.lower() == NODE_RUNTIME_NAME and 'node_modules' in subdirs:
+ subdirs.remove('node_modules')
+ elif lang.lower() == NETCORE_RUNTIME_NAME:
+ if 'bin' in subdirs:
+ subdirs.remove('bin')
+ elif 'obj' in subdirs:
+ subdirs.remove('obj')
+ for filename in files:
+ absname = os.path.abspath(os.path.join(dirname, filename))
+ arcname = absname[len(abs_src) + 1:]
+ zf.write(absname, arcname)
+ return zip_file_path
+
+
+def get_runtime_version_details(file_path, lang_name):
+ version_detected = None
+ version_to_create = None
+ if lang_name.lower() == NETCORE_RUNTIME_NAME:
+ # method returns list in DESC, pick the first
+ version_detected = parse_netcore_version(file_path)[0]
+ version_to_create = detect_netcore_version_tocreate(version_detected)
+ elif lang_name.lower() == DOTNET_RUNTIME_NAME:
+ # method returns list in DESC, pick the first
+ version_detected = parse_dotnet_version(file_path)
+ version_to_create = detect_dotnet_version_tocreate(version_detected)
+ elif lang_name.lower() == NODE_RUNTIME_NAME:
+ version_detected = parse_node_version(file_path)[0]
+ version_to_create = detect_node_version_tocreate(version_detected)
+ elif lang_name.lower() == PYTHON_RUNTIME_NAME:
+ version_detected = "-"
+ version_to_create = PYTHON_VERSION_DEFAULT
+ elif lang_name.lower() == STATIC_RUNTIME_NAME:
+ version_detected = "-"
+ version_to_create = "-"
+ return {'detected': version_detected, 'to_create': version_to_create}
+
+
+def create_resource_group(cmd, rg_name, location):
+ rcf = _resource_client_factory(cmd.cli_ctx)
+ rg_params = ResourceGroup(location=location)
+ return rcf.resource_groups.create_or_update(rg_name, rg_params)
+
+
+def _check_resource_group_exists(cmd, rg_name):
+ rcf = _resource_client_factory(cmd.cli_ctx)
+ return rcf.resource_groups.check_existence(rg_name)
+
+
+def _check_resource_group_supports_os(cmd, rg_name, is_linux):
+ # get all appservice plans from RG
+ client = web_client_factory(cmd.cli_ctx)
+ plans = list(client.app_service_plans.list_by_resource_group(rg_name))
+ for item in plans:
+ # for Linux if an app with reserved==False exists, ASP doesn't support Linux
+ if is_linux and not item.reserved:
+ return False
+ elif not is_linux and item.reserved:
+ return False
+ return True
+
+
+def check_if_asp_exists(cmd, rg_name, asp_name, location):
+ # get all appservice plans from RG
+ client = web_client_factory(cmd.cli_ctx)
+ for item in list(client.app_service_plans.list_by_resource_group(rg_name)):
+ if (item.name.lower() == asp_name.lower() and
+ item.location.replace(" ", "").lower() == location or
+ item.location == location):
+ return True
+ return False
+
+
+def check_app_exists(cmd, rg_name, app_name):
+ client = web_client_factory(cmd.cli_ctx)
+ for item in list(client.web_apps.list_by_resource_group(rg_name)):
+ if item.name.lower() == app_name.lower():
+ return True
+ return False
+
+
+# pylint:disable=unexpected-keyword-arg
+def get_lang_from_content(src_path):
+ import glob
+ # NODE: package.json should exist in the application root dir
+ # NETCORE & DOTNET: *.csproj should exist in the application dir
+ # NETCORE: netcoreapp2.0
+ # DOTNET: v4.5.2
+ runtime_details_dict = dict.fromkeys(['language', 'file_loc', 'default_sku'])
+ package_json_file = os.path.join(src_path, 'package.json')
+ package_python_file = glob.glob("**/*.py", recursive=True)
+ package_netlang_glob = glob.glob("**/*.csproj", recursive=True)
+ static_html_file = glob.glob("**/*.html", recursive=True)
+ if os.path.isfile(package_json_file):
+ runtime_details_dict['language'] = NODE_RUNTIME_NAME
+ runtime_details_dict['file_loc'] = package_json_file
+ runtime_details_dict['default_sku'] = 'B1'
+ elif package_python_file:
+ runtime_details_dict['language'] = PYTHON_RUNTIME_NAME
+ runtime_details_dict['file_loc'] = os.path.join(src_path, package_json_file[0])
+ runtime_details_dict['default_sku'] = 'B1'
+ elif package_netlang_glob:
+ package_netcore_file = os.path.join(src_path, package_netlang_glob[0])
+ runtime_lang = detect_dotnet_lang(package_netcore_file)
+ runtime_details_dict['language'] = runtime_lang
+ runtime_details_dict['file_loc'] = package_netcore_file
+ runtime_details_dict['default_sku'] = 'F1'
+ elif static_html_file:
+ runtime_details_dict['language'] = STATIC_RUNTIME_NAME
+ runtime_details_dict['file_loc'] = static_html_file[0]
+ runtime_details_dict['default_sku'] = 'F1'
+ return runtime_details_dict
+
+
+def detect_dotnet_lang(csproj_path):
+ import xml.etree.ElementTree as ET
+ import re
+ parsed_file = ET.parse(csproj_path)
+ root = parsed_file.getroot()
+ version_lang = ''
+ for target_ver in root.iter('TargetFramework'):
+ version_lang = re.sub(r'([^a-zA-Z\s]+?)', '', target_ver.text)
+ if 'netcore' in version_lang.lower():
+ return NETCORE_RUNTIME_NAME
+ return DOTNET_RUNTIME_NAME
+
+
+def parse_dotnet_version(file_path):
+ version_detected = ['4.7']
+ try:
+ from xml.dom import minidom
+ import re
+ xmldoc = minidom.parse(file_path)
+ framework_ver = xmldoc.getElementsByTagName('TargetFrameworkVersion')
+ target_ver = framework_ver[0].firstChild.data
+ non_decimal = re.compile(r'[^\d.]+')
+ # reduce the version to '5.7.4' from '5.7'
+ if target_ver is not None:
+ # remove the string from the beginning of the version value
+ c = non_decimal.sub('', target_ver)
+ version_detected = c[:3]
+ except: # pylint: disable=bare-except
+ version_detected = version_detected[0]
+ return version_detected
+
+
+def parse_netcore_version(file_path):
+ import xml.etree.ElementTree as ET
+ import re
+ version_detected = ['0.0']
+ parsed_file = ET.parse(file_path)
+ root = parsed_file.getroot()
+ for target_ver in root.iter('TargetFramework'):
+ version_detected = re.findall(r"\d+\.\d+", target_ver.text)
+ # incase of multiple versions detected, return list in descending order
+ version_detected = sorted(version_detected, key=float, reverse=True)
+ return version_detected
+
+
+def parse_node_version(file_path):
+ import json
+ import re
+ with open(file_path) as data_file:
+ data = []
+ for d in find_key_in_json(json.load(data_file), 'node'):
+ non_decimal = re.compile(r'[^\d.]+')
+ # remove the string ~ or > that sometimes exists in version value
+ c = non_decimal.sub('', d)
+ # reduce the version to '6.0' from '6.0.0'
+ data.append(c[:3])
+ version_detected = sorted(data, key=float, reverse=True)
+ return version_detected or ['0.0']
+
+
+def detect_netcore_version_tocreate(detected_ver):
+ if detected_ver in NETCORE_VERSIONS:
+ return detected_ver
+ return NETCORE_VERSION_DEFAULT
+
+
+def detect_dotnet_version_tocreate(detected_ver):
+ min_ver = DOTNET_VERSIONS[0]
+ if detected_ver in DOTNET_VERSIONS:
+ return detected_ver
+ elif detected_ver < min_ver:
+ return min_ver
+ return DOTNET_VERSION_DEFAULT
+
+
+def detect_node_version_tocreate(detected_ver):
+ if detected_ver in NODE_VERSIONS:
+ return detected_ver
+ # get major version & get the closest version from supported list
+ major_ver = float(detected_ver.split('.')[0])
+ if major_ver < 4:
+ return NODE_VERSION_DEFAULT
+ elif major_ver >= 4 and major_ver < 6:
+ return '4.5'
+ elif major_ver >= 6 and major_ver < 8:
+ return '6.9'
+ return NODE_VERSION_DEFAULT
+
+
+def find_key_in_json(json_data, key):
+ for k, v in json_data.items():
+ if key in k:
+ yield v
+ elif isinstance(v, dict):
+ for id_val in find_key_in_json(v, key):
+ yield id_val
+
+
+def set_location(cmd, sku, location):
+ client = web_client_factory(cmd.cli_ctx)
+ if location is None:
+ locs = client.list_geo_regions(sku, True)
+ available_locs = []
+ for loc in locs:
+ available_locs.append(loc.name)
+ location = available_locs[0]
+ else:
+ location = location
+ return location.replace(" ", "").lower()
+
+
+def should_create_new_rg(cmd, default_rg, rg_name, is_linux):
+ if (default_rg and _check_resource_group_exists(cmd, default_rg) and
+ _check_resource_group_supports_os(cmd, default_rg, is_linux)):
+ return False
+ elif (_check_resource_group_exists(cmd, rg_name) and
+ _check_resource_group_supports_os(cmd, rg_name, is_linux)):
+ return False
+ return True
diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py
index 0a76199f1f8..9f610863fed 100644
--- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py
+++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py
@@ -635,6 +635,27 @@
az webapp create -g MyResourceGroup -p MyPlan -n MyUniqueAppName --runtime "node|6.2" --deployment-local-git
"""
+helps['webapp up'] = """
+ type: command
+ short-summary: (Preview) Create and deploy existing local code to the webapp, by running the command from the folder where the code is present.
+ Supports running the command in preview mode using --dryrun parameter. Current supports includes Node, Python,.NET Core, ASP.NET,
+ staticHtml. Node, Python apps are created as Linux apps. .Net Core, ASP.NET and static HTML apps are created as Windows apps.
+ If command is run from an empty folder, an empty windows webapp is created.
+ examples:
+ - name: View the details of the app that will be created, without actually running the operation
+ text: >
+ az webapp up -n MyUniqueAppName --dryrun
+ - name: Create a web app with the default configuration, by running the command from the folder where the code to deployed exists.
+ text: >
+ az webapp up -n MyUniqueAppName
+ - name: Create a web app in a sepcific region, by running the command from the folder where the code to deployed exists.
+ text: >
+ az webapp up -n MyUniqueAppName -l locationName
+ - name: Deploy new code to an app that was originally created using the same command
+ text: >
+ az webapp up -n MyUniqueAppName -l locationName
+"""
+
helps['webapp update'] = """
type: command
short-summary: Update a web app.
diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py
index d6c0af5b4ea..515274198bf 100644
--- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py
+++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py
@@ -326,6 +326,11 @@ def load_arguments(self, _):
c.argument('microsoft_account_client_secret', arg_group='Microsoft', help='AAD V2 Application client secret')
c.argument('microsoft_account_oauth_scopes', nargs='+', help="One or more Microsoft authentification scopes (space-delimited).", arg_group='Microsoft')
+ with self.argument_context('webapp up') as c:
+ c.argument('name', arg_type=webapp_name_arg_type)
+ c.argument('sku', arg_type=sku_arg_type)
+ c.argument('dryrun', help="show summary of the create and deploy operation instead of executing it", default=False, action='store_true')
+
with self.argument_context('functionapp') as c:
c.ignore('app_instance', 'slot')
c.argument('name', arg_type=name_arg_type, id_part='name', help='name of the function app')
diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py
index 2255cd80b11..4bdf45aa913 100644
--- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py
+++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py
@@ -73,6 +73,7 @@ def load_command_table(self, _):
with self.command_group('webapp', webapp_sdk) as g:
g.custom_command('create', 'create_webapp', exception_handler=ex_handler_factory())
+ g.custom_command('up', 'create_deploy_webapp', exception_handler=ex_handler_factory())
g.custom_command('list', 'list_webapp', table_transformer=transform_web_list_output)
g.custom_show_command('show', 'show_webapp', table_transformer=transform_web_output)
g.custom_command('delete', 'delete_webapp')
diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py
index 47120b9096a..bdf4fe08e1f 100644
--- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py
+++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py
@@ -42,7 +42,10 @@
from ._params import AUTH_TYPES, MULTI_CONTAINER_TYPES
from ._client_factory import web_client_factory, ex_handler_factory
from ._appservice_utils import _generic_site_operation
-
+from ._create_util import (zip_contents_from_dir, get_runtime_version_details, create_resource_group,
+ should_create_new_rg, set_location, check_if_asp_exists, check_app_exists,
+ get_lang_from_content)
+from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT, STATIC_RUNTIME_NAME, PYTHON_RUNTIME_NAME)
logger = get_logger(__name__)
@@ -1357,16 +1360,20 @@ def show_container_cd_url(cmd, resource_group_name, name, slot=None):
def view_in_browser(cmd, resource_group_name, name, slot=None, logs=False):
+ url = _get_url(cmd, resource_group_name, name, slot)
+ open_page_in_browser(url)
+ if logs:
+ get_streaming_log(cmd, resource_group_name, name, provider=None, slot=slot)
+
+
+def _get_url(cmd, resource_group_name, name, slot=None):
site = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get', slot)
if not site:
raise CLIError("'{}' app doesn't exist".format(name))
url = site.enabled_host_names[0] # picks the custom domain URL incase a domain is assigned
ssl_host = next((h for h in site.host_name_ssl_states
if h.ssl_state != SslState.disabled), None)
- url = ('https' if ssl_host else 'http') + '://' + url
- open_page_in_browser(url)
- if logs:
- get_streaming_log(cmd, resource_group_name, name, provider=None, slot=slot)
+ return ('https' if ssl_host else 'http') + '://' + url
# TODO: expose new blob suport
@@ -1960,12 +1967,12 @@ def list_locations(cmd, sku, linux_workers_enabled=None):
def _check_zip_deployment_status(deployment_status_url, authorization, timeout=None):
import requests
import time
- total_trials = (int(timeout) // 30) if timeout else 10
+ total_trials = (int(timeout) // 2) if timeout else 450
for _num_trials in range(total_trials):
- time.sleep(30)
+ time.sleep(2)
response = requests.get(deployment_status_url, headers=authorization)
res_dict = response.json()
- if res_dict.get('status', 0) == 5:
+ if res_dict.get('status', 0) == 3:
logger.warning("Zip deployment failed status %s", res_dict['status_text'])
break
elif res_dict.get('status', 0) == 4:
@@ -1974,8 +1981,8 @@ def _check_zip_deployment_status(deployment_status_url, authorization, timeout=N
logger.info(res_dict['progress']) # show only in debug mode, customers seem to find this confusing
# if the deployment is taking longer than expected
if res_dict.get('status', 0) != 4:
- logger.warning("""Deployment is taking longer than expected. Please verify status at '%s'
- beforing launching the app""", deployment_status_url)
+ raise ValueError("""Deployment is taking longer than expected. Please verify
+ status at '{}' beforing launching the app""".format(deployment_status_url))
return res_dict
@@ -2033,3 +2040,154 @@ def get_history_triggered_webjob(cmd, resource_group_name, name, webjob_name, sl
if slot:
return client.web_apps.list_triggered_web_job_history_slot(resource_group_name, name, webjob_name, slot)
return client.web_apps.list_triggered_web_job_history(resource_group_name, name, webjob_name)
+
+
+def create_deploy_webapp(cmd, name, location=None, sku=None, dryrun=False): # pylint: disable=too-many-statements
+ import os
+ client = web_client_factory(cmd.cli_ctx)
+ # the code to deploy is expected to be the current directory the command is running from
+ src_dir = os.getcwd()
+ # if dir is empty, show a message in dry run
+ do_deployment = False if os.listdir(src_dir) == [] else True
+ _create_new_rg = True
+ _create_new_asp = True
+ _create_new_app = True
+ _set_build_appSetting = False
+
+ # determine the details for app to be created from src contents
+ lang_details = get_lang_from_content(src_dir)
+ # we support E2E create and deploy for selected stacks, any other stack, set defaults for os & runtime
+ # and skip deployment
+ if lang_details['language'] is None:
+ do_deployment = False
+ sku = sku | 'F1'
+ os_val = OS_DEFAULT
+ detected_version = '-'
+ runtime_version = '-'
+ else:
+ # update SKU to user set value
+ if sku is None:
+ sku = lang_details.get("default_sku")
+ else:
+ sku = sku
+ language = lang_details.get("language")
+ is_skip_build = language.lower() == STATIC_RUNTIME_NAME
+ os_val = "Linux" if language.lower() == NODE_RUNTIME_NAME \
+ or language.lower() == PYTHON_RUNTIME_NAME else OS_DEFAULT
+ # detect the version
+ data = get_runtime_version_details(lang_details.get('file_loc'), language)
+ version_used_create = data.get('to_create')
+ detected_version = data.get('detected')
+ runtime_version = "{}|{}".format(language, version_used_create) if \
+ version_used_create != "-" else version_used_create
+
+ full_sku = get_sku_name(sku)
+ loc_name = set_location(cmd, sku, location)
+ is_linux = True if os_val == 'Linux' else False
+ asp = "appsvc_asp_{}_{}".format(os_val, loc_name)
+ rg_name = "appsvc_rg_{}_{}".format(os_val, loc_name)
+ # Resource group: check if default RG is set
+ default_rg = cmd.cli_ctx.config.get('defaults', 'group', fallback=None)
+ _create_new_rg = should_create_new_rg(cmd, default_rg, rg_name, is_linux)
+
+ src_path = "{}".format(src_dir.replace("\\", "\\\\"))
+ rg_str = "{}".format(rg_name)
+ dry_run_str = r""" {
+ "name" : "%s",
+ "serverfarm" : "%s",
+ "resourcegroup" : "%s",
+ "sku": "%s",
+ "os": "%s",
+ "location" : "%s",
+ "src_path" : "%s",
+ "version_detected": "%s",
+ "version_to_create": "%s"
+ }
+ """ % (name, asp, rg_str, full_sku, os_val, location, src_path,
+ detected_version, runtime_version)
+ create_json = json.loads(dry_run_str)
+
+ if dryrun:
+ logger.warning("Web app will be created with the below configuration,re-run command "
+ "without the --dryrun flag to create & deploy a new app")
+ return create_json
+
+ # create RG if the RG doesn't already exist
+ if _create_new_rg:
+ logger.warning("Creating Resource group '%s' ...", rg_name)
+ create_resource_group(cmd, rg_name, location)
+ logger.warning("Resource group creation complete")
+ _create_new_asp = True
+ else:
+ logger.warning("Resource group '%s' already exists.", rg_name)
+ _create_new_asp = check_if_asp_exists(cmd, rg_name, asp, location)
+ # create new ASP if an existing one cannot be used
+ if _create_new_asp:
+ logger.warning("Creating App service plan '%s' ...", asp)
+ sku_def = SkuDescription(tier=full_sku, name=sku, capacity=(1 if is_linux else None))
+ plan_def = AppServicePlan(location=loc_name, app_service_plan_name=asp,
+ sku=sku_def, reserved=(is_linux or None))
+ client.app_service_plans.create_or_update(rg_name, asp, plan_def)
+ logger.warning("App service plan creation complete")
+ _create_new_app = True
+ else:
+ logger.warning("App service plan '%s' already exists.", asp)
+ _create_new_asp = False
+ _create_new_app = check_app_exists(cmd, rg_name, name)
+ # create the app
+ if _create_new_app:
+ logger.warning("Creating app '%s' ....", name)
+ create_webapp(cmd, rg_name, name, asp, runtime_version if is_linux else None)
+ logger.warning("Webapp creation complete")
+ _set_build_appSetting = True
+ else:
+ logger.warning("App '%s' already exists", name)
+ if do_deployment:
+ # setting the appsettings causes a app restart so we avoid if not needed
+ _app_settings = get_app_settings(cmd, rg_name, name)
+ if all(not d for d in _app_settings):
+ _set_build_appSetting = True
+ elif '"name": "SCM_DO_BUILD_DURING_DEPLOYMENT", "value": "true"' not in json.dumps(_app_settings[0]):
+ _set_build_appSetting = True
+ else:
+ _set_build_appSetting = False
+
+ # update create_json to include the app_url
+ url = _get_url(cmd, rg_name, name)
+
+ if do_deployment and not is_skip_build and _set_build_appSetting:
+ # setting to build after deployment
+ logger.warning("Updating app settings to enable build after deployment")
+ update_app_settings(cmd, rg_name, name, ["SCM_DO_BUILD_DURING_DEPLOYMENT=true"])
+ # work around until the timeout limits issue for linux is investigated & fixed
+ # wakeup kudu, by making an SCM call
+ import time
+ time.sleep(5)
+ _ping_scm_site(cmd, rg_name, name)
+
+ logger.warning("Creating zip with contents of dir %s ...", src_dir)
+ # zip contents & deploy
+ zip_file_path = zip_contents_from_dir(src_dir, language)
+
+ logger.warning("Preparing to deploy %s contents to app.",
+ '' if is_skip_build else 'and build')
+ enable_zip_deploy(cmd, rg_name, name, zip_file_path)
+ # Remove the file afer deployment, handling exception if user removed the file manually
+ try:
+ os.remove(zip_file_path)
+ except OSError:
+ pass
+ create_json.update({'app_url': url})
+ logger.warning("All done.")
+ return create_json
+
+
+def _ping_scm_site(cmd, resource_group, name):
+ # wakeup kudu, by making an SCM call
+ import requests
+ # work around until the timeout limits issue for linux is investigated & fixed
+ user_name, password = _get_site_credential(cmd.cli_ctx, resource_group, name)
+ scm_url = _get_scm_url(cmd, resource_group, name)
+ import urllib3
+ authorization = urllib3.util.make_headers(basic_auth='{}:{}'.format(user_name, password))
+ requests.get(scm_url + '/api/settings', headers=authorization)