Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@
import os

NODE_VERSION_DEFAULT = "10.14"
NODE_VERSION_NEWER = "12-lts"
NODE_EXACT_VERSION_DEFAULT = "10.14.1"
NETCORE_VERSION_DEFAULT = "3.1"
DOTNET_VERSION_DEFAULT = "4.7"
ASPDOTNET_VERSION_DEFAULT = "4.8"
DOTNET_VERSION_DEFAULT = "5.0"
DOTNET_TARGET_FRAMEWORK_STRING = "net5.0"
PYTHON_VERSION_DEFAULT = "3.7"
NETCORE_RUNTIME_NAME = "dotnetcore"
DOTNET_RUNTIME_NAME = "aspnet"
ASPDOTNET_RUNTIME_NAME = "aspnet"
DOTNET_RUNTIME_NAME = "dotnet"
NODE_RUNTIME_NAME = "node"
PYTHON_RUNTIME_NAME = "python"
OS_DEFAULT = "Windows"
STATIC_RUNTIME_NAME = "static" # not an official supported runtime but used for CLI logic
NODE_VERSIONS = ['10.6', '10.14']
PYTHON_VERSIONS = ['3.8', '3.7', '3.6']
NETCORE_VERSIONS = ['2.1', '3.1']
DOTNET_VERSIONS = ['3.5', '4.7']
DOTNET_VERSIONS = ['3.5', '4.8']
LINUX_SKU_DEFAULT = "P1V2"
FUNCTIONS_VERSIONS = ['2', '3']
FUNCTIONS_STACKS_API_JSON_PATHS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
from azure.mgmt.web.models import SkuDescription

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, LINUX_SKU_DEFAULT, OS_DEFAULT)
NODE_VERSIONS, NETCORE_RUNTIME_NAME, NODE_RUNTIME_NAME, ASPDOTNET_RUNTIME_NAME,
ASPDOTNET_VERSION_DEFAULT, DOTNET_VERSIONS, STATIC_RUNTIME_NAME,
PYTHON_RUNTIME_NAME, PYTHON_VERSION_DEFAULT, LINUX_SKU_DEFAULT, OS_DEFAULT,
NODE_VERSION_NEWER, DOTNET_RUNTIME_NAME, DOTNET_VERSION_DEFAULT,
DOTNET_TARGET_FRAMEWORK_STRING)

logger = get_logger(__name__)

Expand Down Expand Up @@ -73,11 +75,14 @@ def zip_contents_from_dir(dirPath, lang):
def get_runtime_version_details(file_path, lang_name):
version_detected = None
version_to_create = None
if lang_name.lower() == NETCORE_RUNTIME_NAME:
if lang_name.lower() == DOTNET_RUNTIME_NAME:
version_detected = DOTNET_VERSION_DEFAULT
version_to_create = DOTNET_VERSION_DEFAULT
elif 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:
elif lang_name.lower() == ASPDOTNET_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)
Expand Down Expand Up @@ -187,11 +192,17 @@ def detect_dotnet_lang(csproj_path):
parsed_file = ET.parse(csproj_path)
root = parsed_file.getroot()
version_lang = ''
version_full = ''
for target_ver in root.iter('TargetFramework'):
version_full = target_ver.text
version_full = ''.join(version_full.split()).lower()
version_lang = re.sub(r'([^a-zA-Z\s]+?)', '', target_ver.text)

if version_full and version_full.startswith(DOTNET_TARGET_FRAMEWORK_STRING):
return DOTNET_RUNTIME_NAME
if 'netcore' in version_lang.lower():
return NETCORE_RUNTIME_NAME
return DOTNET_RUNTIME_NAME
return ASPDOTNET_RUNTIME_NAME


def parse_dotnet_version(file_path):
Expand Down Expand Up @@ -261,7 +272,7 @@ def detect_dotnet_version_tocreate(detected_ver):
return detected_ver
if detected_ver < min_ver:
return min_ver
return DOTNET_VERSION_DEFAULT
return ASPDOTNET_VERSION_DEFAULT


def detect_node_version_tocreate(detected_ver):
Expand All @@ -274,7 +285,7 @@ def detect_node_version_tocreate(detected_ver):
if major_ver <= 11:
node_ver = NODE_VERSION_DEFAULT
else:
node_ver = '12.9'
node_ver = NODE_VERSION_NEWER
return node_ver


Expand Down
98 changes: 81 additions & 17 deletions src/azure-cli/azure/cli/command_modules/appservice/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,11 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi
site_config.app_command_line = startup_file

if runtime:
site_config.linux_fx_version = runtime
match = helper.resolve(runtime)
if not match:
raise CLIError("Linux Runtime '{}' is not supported."
" Please invoke 'az webapp list-runtimes --linux' to cross check".format(runtime))
match['setter'](cmd=cmd, stack=match, site_config=site_config)
elif deployment_container_image_name:
site_config.linux_fx_version = _format_fx_version(deployment_container_image_name)
if name_validation.name_available:
Expand Down Expand Up @@ -176,7 +176,7 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi
current_stack = get_current_stack_from_runtime(runtime)

else: # windows webapp without runtime specified
if name_validation.name_available:
if name_validation.name_available: # If creating new webapp
site_config.app_settings.append(NameValuePair(name="WEBSITE_NODE_DEFAULT_VERSION",
value=node_default_version))

Expand All @@ -197,9 +197,7 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi
webapp = LongRunningOperation(cmd.cli_ctx)(poller)

if current_stack:
app_metadata = client.web_apps.list_metadata(resource_group_name, name)
app_metadata.properties["CURRENT_STACK"] = current_stack
client.web_apps.update_metadata(resource_group_name, name, kind="app", properties=app_metadata.properties)
_update_webapp_current_stack_property_if_needed(cmd, resource_group_name, name, current_stack)

# Ensure SCC operations follow right after the 'create', no precedent appsetting update commands
_set_remote_or_local_git(cmd, webapp, resource_group_name, name, deployment_source_url,
Expand Down Expand Up @@ -2563,7 +2561,15 @@ def update_site_appsettings(cmd, stack, site_config):
NameValuePair = cmd.get_models('NameValuePair')
if site_config.app_settings is None:
site_config.app_settings = []
site_config.app_settings += [NameValuePair(name=k, value=v) for k, v in stack['configs'].items()]

for k, v in stack['configs'].items():
already_in_appsettings = False
for app_setting in site_config.app_settings:
if app_setting.name == k:
already_in_appsettings = True
app_setting.value = v
if not already_in_appsettings:
site_config.app_settings.append(NameValuePair(name=k, value=v))
return site_config

def _load_stacks_hardcoded(self):
Expand All @@ -2572,6 +2578,8 @@ def _load_stacks_hardcoded(self):
result = []
if self._linux:
result = get_file_json(RUNTIME_STACKS)['linux']
for r in result:
r['setter'] = _StackRuntimeHelper.update_site_config
else: # Windows stacks
result = get_file_json(RUNTIME_STACKS)['windows']
for r in result:
Expand Down Expand Up @@ -3680,18 +3688,26 @@ def webapp_up(cmd, name, resource_group_name=None, plan=None, location=None, sku
using_webapp_up=True, language=language)
_configure_default_logging(cmd, rg_name, name)
else: # for existing app if we might need to update the stack runtime settings
helper = _StackRuntimeHelper(cmd, client, linux=_is_linux)
match = helper.resolve(runtime_version)

if os_name.lower() == 'linux' and site_config.linux_fx_version != runtime_version:
logger.warning('Updating runtime version from %s to %s',
site_config.linux_fx_version, runtime_version)
update_site_configs(cmd, rg_name, name, linux_fx_version=runtime_version)
logger.warning('Waiting for runtime version to propagate ...')
time.sleep(30) # wait for kudu to get updated runtime before zipdeploy. Currently no way to poll for this
elif os_name.lower() == 'windows' and site_config.windows_fx_version != runtime_version:
logger.warning('Updating runtime version from %s to %s',
site_config.windows_fx_version, runtime_version)
update_site_configs(cmd, rg_name, name, windows_fx_version=runtime_version)
logger.warning('Waiting for runtime version to propagate ...')
time.sleep(30) # wait for kudu to get updated runtime before zipdeploy. Currently no way to poll for this
if match and site_config.linux_fx_version != match['configs']['linux_fx_version']:
logger.warning('Updating runtime version from %s to %s',
site_config.linux_fx_version, match['configs']['linux_fx_version'])
update_site_configs(cmd, rg_name, name, linux_fx_version=match['configs']['linux_fx_version'])
logger.warning('Waiting for runtime version to propagate ...')
time.sleep(30) # wait for kudu to get updated runtime before zipdeploy. No way to poll for this
elif not match:
logger.warning('Updating runtime version from %s to %s',
site_config.linux_fx_version, runtime_version)
update_site_configs(cmd, rg_name, name, linux_fx_version=runtime_version)
logger.warning('Waiting for runtime version to propagate ...')
time.sleep(30) # wait for kudu to get updated runtime before zipdeploy. No way to poll for this
elif os_name.lower() == 'windows':
# may need to update stack runtime settings. For node its site_config.app_settings, otherwise site_config
if match:
_update_app_settings_for_windows_if_needed(cmd, rg_name, name, match, site_config, runtime_version)
create_json['runtime_version'] = runtime_version
# Zip contents & Deploy
logger.warning("Creating zip with contents of dir %s ...", src_dir)
Expand Down Expand Up @@ -3719,6 +3735,54 @@ def webapp_up(cmd, name, resource_group_name=None, plan=None, location=None, sku
return create_json


def _update_app_settings_for_windows_if_needed(cmd, rg_name, name, match, site_config, runtime_version):
update_needed = False
if 'node' in runtime_version:
settings = []
for k, v in match['configs'].items():
for app_setting in site_config.app_settings:
if app_setting.name == k and app_setting.value != v:
update_needed = True
settings.append('%s=%s', k, v)
if update_needed:
logger.warning('Updating runtime version to %s', runtime_version)
update_app_settings(cmd, rg_name, name, settings=settings, slot=None, slot_settings=None)
else:
for k, v in match['configs'].items():
if getattr(site_config, k, None) != v:
update_needed = True
setattr(site_config, k, v)
if update_needed:
logger.warning('Updating runtime version to %s', runtime_version)
update_site_configs(cmd,
rg_name,
name,
net_framework_version=site_config.net_framework_version,
php_version=site_config.php_version,
python_version=site_config.python_version,
java_version=site_config.java_version,
java_container=site_config.java_container,
java_container_version=site_config.java_container_version)

current_stack = get_current_stack_from_runtime(runtime_version)
_update_webapp_current_stack_property_if_needed(cmd, rg_name, name, current_stack)

if update_needed:
logger.warning('Waiting for runtime version to propagate ...')
time.sleep(30) # wait for kudu to get updated runtime before zipdeploy. No way to poll for this


def _update_webapp_current_stack_property_if_needed(cmd, resource_group, name, current_stack):
if not current_stack:
return
# portal uses this current_stack value to display correct runtime for windows webapps
client = web_client_factory(cmd.cli_ctx)
app_metadata = client.web_apps.list_metadata(resource_group, name)
if 'CURRENT_STACK' not in app_metadata.properties or app_metadata.properties["CURRENT_STACK"] != current_stack:
app_metadata.properties["CURRENT_STACK"] = current_stack
client.web_apps.update_metadata(resource_group, name, kind="app", properties=app_metadata.properties)


def _ping_scm_site(cmd, resource_group, name, instance=None):
from azure.cli.core.util import should_disable_connection_verify
# wake up kudu, by making an SCM call
Expand Down
Loading