in src/appservice-kube/azext_appservice_kube/custom.py [0:0]
def create_functionapp(cmd, resource_group_name, name, storage_account=None, plan=None,
os_type=None, functions_version=None, runtime=None, runtime_version=None,
consumption_plan_location=None, app_insights=None, app_insights_key=None,
disable_app_insights=None, deployment_source_url=None,
deployment_source_branch='master', deployment_local_git=None,
docker_registry_server_password=None, docker_registry_server_user=None,
deployment_container_image_name=None, tags=None, assign_identities=None,
role='Contributor', scope=None,
custom_location=None, min_worker_count=None, max_worker_count=None):
# pylint: disable=too-many-statements, too-many-branches
if functions_version is None:
logger.warning("No functions version specified so defaulting to 3. In the future, specifying a version will "
"be required. To create a 3.x function you would pass in the flag `--functions-version 3`")
functions_version = '3'
if deployment_source_url and deployment_local_git:
raise MutuallyExclusiveArgumentError('usage error: --deployment-source-url <url> | --deployment-local-git')
if not plan and not consumption_plan_location and not custom_location:
raise RequiredArgumentMissingError("Either Plan, Consumption Plan or Custom Location must be specified")
if consumption_plan_location and custom_location:
raise MutuallyExclusiveArgumentError("Consumption Plan and Custom Location cannot be used together")
if consumption_plan_location and plan:
raise MutuallyExclusiveArgumentError("Consumption Plan and Plan cannot be used together")
from azure.mgmt.web.models import Site
SiteConfig, NameValuePair, SkuDescription = cmd.get_models('SiteConfig', 'NameValuePair', 'SkuDescription')
docker_registry_server_url = parse_docker_image_name(deployment_container_image_name)
disable_app_insights = disable_app_insights == "true"
custom_location = _get_custom_location_id(cmd, custom_location, resource_group_name)
site_config = SiteConfig(app_settings=[])
client = web_client_factory(cmd.cli_ctx)
functionapp_def = Site(location=None, site_config=site_config, tags=tags)
plan_info = None
if runtime is not None:
runtime = runtime.lower()
if consumption_plan_location:
locations = list_consumption_locations(cmd)
location = next((loc for loc in locations if loc['name'].lower() == consumption_plan_location.lower()), None)
if location is None:
raise ValidationError("Location is invalid. Use: az functionapp list-consumption-locations")
functionapp_def.location = consumption_plan_location
functionapp_def.kind = 'functionapp'
# if os_type is None, the os type is windows
is_linux = bool(os_type and os_type.lower() == LINUX_OS_NAME)
else: # apps with SKU based plan
_should_create_new_plan = _should_create_new_appservice_plan_for_k8se(cmd,
name, custom_location,
plan, resource_group_name)
if _should_create_new_plan:
plan = generate_default_app_service_plan_name(name)
logger.warning("Plan not specified. Creating Plan '%s' with sku '%s'", plan, KUBE_DEFAULT_SKU)
create_app_service_plan(cmd=cmd, resource_group_name=resource_group_name,
name=plan, is_linux=True, hyper_v=False, custom_location=custom_location,
per_site_scaling=True, number_of_workers=1)
if custom_location and plan:
if not _validate_asp_and_custom_location_kube_envs_match(cmd, resource_group_name, custom_location, plan):
raise ValidationError("Custom location's kube environment "
"and App Service Plan's kube environment don't match")
elif custom_location and not plan:
app_details = get_app_details(cmd, name, resource_group_name)
if app_details is not None:
plan = app_details.server_farm_id
if is_valid_resource_id(plan):
parse_result = parse_resource_id(plan)
plan_info = client.app_service_plans.get(parse_result['resource_group'], parse_result['name'])
else:
plan_info = client.app_service_plans.get(resource_group_name, plan)
if not plan_info:
raise ResourceNotFoundError("The plan '{}' doesn't exist".format(plan))
location = plan_info.location
is_linux = bool(plan_info.reserved)
functionapp_def.server_farm_id = plan
functionapp_def.location = location
if functions_version == '2' and functionapp_def.location in FUNCTIONS_NO_V2_REGIONS:
raise ValidationError("2.x functions are not supported in this region. To create a 3.x function, "
"pass in the flag '--functions-version 3'")
if is_linux and not runtime and (consumption_plan_location or not deployment_container_image_name):
raise ArgumentUsageError(
"usage error: --runtime RUNTIME required for linux functions apps without custom image.")
if runtime is None and runtime_version is not None:
raise ArgumentUsageError('Must specify --runtime to use --runtime-version')
is_kube = _is_function_kube(custom_location, plan_info, SkuDescription)
if not storage_account and not is_kube:
raise ValidationError("--storage-account required for non-kubernetes function apps")
runtime_helper = _FunctionAppStackRuntimeHelper(cmd, linux=is_linux, windows=not is_linux)
matched_runtime = runtime_helper.resolve("dotnet" if not runtime else runtime,
runtime_version, functions_version, is_linux)
if is_kube:
if min_worker_count is not None:
site_config.number_of_workers = min_worker_count
if max_worker_count is not None:
site_config.app_settings.append(NameValuePair(name='K8SE_APP_MAX_INSTANCE_COUNT', value=max_worker_count))
site_config_dict = matched_runtime.site_config_dict
app_settings_dict = matched_runtime.app_settings_dict
con_string = None
if storage_account:
con_string = _validate_and_get_connection_string(cmd.cli_ctx, resource_group_name, storage_account)
if is_kube:
functionapp_def.enable_additional_properties_sending()
# if Custom Location provided, use that for Extended Location Envelope. Otherwise, get Custom Location from ASP
if custom_location:
custom_location_id = _get_custom_location_id_from_custom_location(cmd, custom_location, resource_group_name)
if custom_location_id:
extended_loc = {'name': custom_location_id, 'type': 'CustomLocation'}
functionapp_def.additional_properties["extendedLocation"] = extended_loc
else:
extended_loc = plan_info.additional_properties["extendedLocation"]
functionapp_def.additional_properties["extendedLocation"] = extended_loc
functionapp_def.kind = KUBE_FUNCTION_APP_KIND
functionapp_def.reserved = True
site_config.app_settings.append(NameValuePair(name='WEBSITES_PORT', value='80'))
site_config.app_settings.append(NameValuePair(name='MACHINEKEY_DecryptionKey',
value=str(hexlify(urandom(32)).decode()).upper()))
if deployment_container_image_name:
functionapp_def.kind = KUBE_FUNCTION_CONTAINER_APP_KIND
site_config.app_settings.append(NameValuePair(name='DOCKER_CUSTOM_IMAGE_NAME',
value=deployment_container_image_name))
site_config.app_settings.append(NameValuePair(name='FUNCTION_APP_EDIT_MODE', value='readOnly'))
site_config.linux_fx_version = _format_fx_version(deployment_container_image_name)
site_config.app_settings.append(NameValuePair(name='DOCKER_REGISTRY_SERVER_URL',
value=docker_registry_server_url))
if docker_registry_server_user is not None and docker_registry_server_password is not None:
site_config.app_settings.append(NameValuePair(name='DOCKER_REGISTRY_SERVER_USERNAME',
value=docker_registry_server_user))
site_config.app_settings.append(NameValuePair(name='DOCKER_REGISTRY_SERVER_PASSWORD',
value=docker_registry_server_password))
else:
site_config.app_settings.append(NameValuePair(name='WEBSITES_ENABLE_APP_SERVICE_STORAGE', value='true'))
site_config.linux_fx_version = _get_linux_fx_kube_functionapp(runtime, runtime_version)
elif is_linux:
functionapp_def.kind = 'functionapp,linux'
functionapp_def.reserved = True
is_consumption = consumption_plan_location is not None
if not is_consumption:
site_config.app_settings.append(NameValuePair(name='MACHINEKEY_DecryptionKey',
value=str(hexlify(urandom(32)).decode()).upper()))
if deployment_container_image_name:
functionapp_def.kind = 'functionapp,linux,container'
site_config.app_settings.append(NameValuePair(name='DOCKER_CUSTOM_IMAGE_NAME',
value=deployment_container_image_name))
site_config.app_settings.append(NameValuePair(name='FUNCTION_APP_EDIT_MODE', value='readOnly'))
site_config.app_settings.append(NameValuePair(name='WEBSITES_ENABLE_APP_SERVICE_STORAGE',
value='false'))
site_config.linux_fx_version = _format_fx_version(deployment_container_image_name)
# clear all runtime specific configs and settings
site_config_dict.use32_bit_worker_process = False
app_settings_dict = {}
# ensure that app insights is created if not disabled
matched_runtime.app_insights = True
else:
site_config.app_settings.append(NameValuePair(name='WEBSITES_ENABLE_APP_SERVICE_STORAGE',
value='true'))
else:
functionapp_def.kind = 'functionapp'
# set site configs
for prop, value in site_config_dict.as_dict().items():
snake_case_prop = _convert_camel_to_snake_case(prop)
setattr(site_config, snake_case_prop, value)
# temporary workaround for dotnet-isolated linux consumption apps
if is_linux and consumption_plan_location is not None and runtime == 'dotnet-isolated':
site_config.linux_fx_version = ''
# adding app settings
for app_setting, value in app_settings_dict.items():
site_config.app_settings.append(NameValuePair(name=app_setting, value=value))
site_config.app_settings.append(NameValuePair(name='FUNCTIONS_EXTENSION_VERSION',
value=_get_extension_version_functionapp(functions_version)))
if con_string:
site_config.app_settings.append(NameValuePair(name='AzureWebJobsStorage', value=con_string))
# If plan is not consumption or elastic premium, we need to set always on
if consumption_plan_location is None and not is_plan_elastic_premium(cmd, plan_info):
site_config.always_on = True
# If plan is elastic premium or consumption, we need these app settings
if is_plan_elastic_premium(cmd, plan_info) or consumption_plan_location is not None:
if con_string:
site_config.app_settings.append(NameValuePair(name='WEBSITE_CONTENTAZUREFILECONNECTIONSTRING',
value=con_string))
site_config.app_settings.append(NameValuePair(name='WEBSITE_CONTENTSHARE', value=_get_content_share_name(name)))
create_app_insights = False
if app_insights_key is not None:
site_config.app_settings.append(NameValuePair(name='APPINSIGHTS_INSTRUMENTATIONKEY',
value=app_insights_key))
elif app_insights is not None:
instrumentation_key = get_app_insights_key(cmd.cli_ctx, resource_group_name, app_insights)
site_config.app_settings.append(NameValuePair(name='APPINSIGHTS_INSTRUMENTATIONKEY',
value=instrumentation_key))
elif disable_app_insights or not matched_runtime.app_insights:
# set up dashboard if no app insights
if con_string:
site_config.app_settings.append(NameValuePair(name='AzureWebJobsDashboard', value=con_string))
elif not disable_app_insights and matched_runtime.app_insights:
create_app_insights = True
poller = client.web_apps.begin_create_or_update(resource_group_name, name, functionapp_def)
functionapp = LongRunningOperation(cmd.cli_ctx)(poller)
if consumption_plan_location and is_linux:
logger.warning("Your Linux function app '%s', that uses a consumption plan has been successfully "
"created but is not active until content is published using "
"Azure Portal or the Functions Core Tools.", name)
else:
_set_remote_or_local_git(cmd, functionapp, resource_group_name, name, deployment_source_url,
deployment_source_branch, deployment_local_git)
if create_app_insights:
try:
try_create_application_insights(cmd, functionapp)
except Exception: # pylint: disable=broad-except
logger.warning('Error while trying to create and configure an Application Insights for the Function App. '
'Please use the Azure Portal to create and configure the Application Insights, if needed.')
if con_string:
update_app_settings(cmd, functionapp.resource_group, functionapp.name,
['AzureWebJobsDashboard={}'.format(con_string)])
if deployment_container_image_name:
update_container_settings_functionapp(cmd, resource_group_name, name, docker_registry_server_url,
deployment_container_image_name, docker_registry_server_user,
docker_registry_server_password)
if assign_identities is not None:
identity = assign_identity(cmd, resource_group_name, name, assign_identities,
role, None, scope)
functionapp.identity = identity
return functionapp