@@ -385,10 +385,12 @@ def create_webapp(cmd, resource_group_name, name, plan, runtime=None, startup_fi
385385
386386 _enable_basic_auth(cmd, name, None, resource_group_name, basic_auth.lower())
387387 # Only suggest deployment command when no deployment method is already configured
388- if not using_webapp_up and not any([container_image_name, deployment_container_image_name,
389- multicontainer_config_type, sitecontainers_app,
390- deployment_source_url, deployment_local_git]):
391- logger.warning("Webapp '%s' created. Deploy your code with: az webapp deploy", name)
388+ if not using_webapp_up:
389+ if not any([container_image_name, deployment_container_image_name,
390+ multicontainer_config_type, sitecontainers_app,
391+ deployment_source_url, deployment_local_git]):
392+ logger.warning("Webapp '%s' created. Deploy your code with: az webapp deploy", name)
393+ _log_webapp_status_tip(name, resource_group_name)
392394 return webapp
393395
394396
@@ -2460,6 +2462,85 @@ def show_app(cmd, resource_group_name, name, slot=None):
24602462 return app
24612463
24622464
2465+ def _log_webapp_status_tip(name, resource_group_name):
2466+ logger.warning("Tip: run 'az webapp status --name %s --resource-group %s' "
2467+ "to see per-instance runtime status.",
2468+ name, resource_group_name)
2469+
2470+
2471+ def _extract_webapp_status_items(result):
2472+ # The siteStatus ARM API returns a ResponseMessageEnvelope whose 'properties'
2473+ # holds the list of per-instance SiteRuntimeStatusOnWorker objects.
2474+ if isinstance(result, dict):
2475+ properties = result.get('properties')
2476+ if isinstance(properties, list):
2477+ return properties
2478+ if isinstance(properties, dict):
2479+ return [properties]
2480+ return []
2481+
2482+
2483+ def format_webapp_status_output(result):
2484+ from collections import OrderedDict
2485+
2486+ items = _extract_webapp_status_items(result)
2487+ # LastError is a nullable field on the backend SiteRuntimeStatusOnWorker contract,
2488+ # so the error columns (LastError, LastErrorDetails, LastErrorTimestamp) are only
2489+ # surfaced when at least one instance reports a LastError. Details and DetailsLevel
2490+ # are always serialized by the backend and are always shown.
2491+ show_errors = any(item.get('lastError') for item in items)
2492+
2493+ rows = []
2494+ for item in items:
2495+ row = OrderedDict([
2496+ ('InstanceId', item.get('instanceId')),
2497+ ('State', item.get('state')),
2498+ ('Action', item.get('action'))
2499+ ])
2500+ if show_errors:
2501+ row['LastError'] = item.get('lastError')
2502+ row['LastErrorDetails'] = item.get('lastErrorDetails')
2503+ row['LastErrorTimestamp'] = item.get('lastErrorTimestamp')
2504+ row['Details'] = item.get('details')
2505+ row['DetailsLevel'] = item.get('detailsLevel')
2506+ rows.append(row)
2507+ return rows
2508+
2509+
2510+ def show_webapp_status(cmd, resource_group_name, name, slot=None, instance=None):
2511+ from azure.cli.core.commands.client_factory import get_subscription_id
2512+
2513+ client = web_client_factory(cmd.cli_ctx)
2514+ subscription_id = get_subscription_id(cmd.cli_ctx)
2515+ api_version = client.DEFAULT_API_VERSION
2516+ slot_segment = '/slots/{}'.format(slot) if slot else ''
2517+ # When an instance is requested, call the dedicated siteStatus/{instanceId} route
2518+ # so the backend resolves and validates the instance (returning 404 if missing).
2519+ instance_segment = '/{}'.format(instance) if instance else ''
2520+ request_url = (
2521+ '{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Web/sites/{}{}/siteStatus{}'
2522+ '?api-version={}'.format(
2523+ cmd.cli_ctx.cloud.endpoints.resource_manager,
2524+ subscription_id,
2525+ resource_group_name,
2526+ name,
2527+ slot_segment,
2528+ instance_segment,
2529+ api_version
2530+ )
2531+ )
2532+
2533+ try:
2534+ return send_raw_request(cmd.cli_ctx, 'GET', request_url).json()
2535+ except HttpResponseError as ex:
2536+ if instance and ex.status_code == 404:
2537+ scope = 'webapp and slot' if slot else 'webapp'
2538+ raise ResourceNotFoundError(
2539+ "Instance '{}' was not found for this {}. "
2540+ "Run 'az webapp list-instances' to see available instance IDs.".format(instance, scope))
2541+ raise
2542+
2543+
24632544def _list_app(cli_ctx, resource_group_name=None, show_details=False):
24642545 client = web_client_factory(cli_ctx)
24652546 if resource_group_name:
@@ -3583,11 +3664,15 @@ def stop_webapp(cmd, resource_group_name, name, slot=None):
35833664
35843665
35853666def start_webapp(cmd, resource_group_name, name, slot=None):
3586- return _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'start', slot)
3667+ result = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'start', slot)
3668+ _log_webapp_status_tip(name, resource_group_name)
3669+ return result
35873670
35883671
35893672def restart_webapp(cmd, resource_group_name, name, slot=None):
3590- return _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'restart', slot)
3673+ result = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'restart', slot)
3674+ _log_webapp_status_tip(name, resource_group_name)
3675+ return result
35913676
35923677
35933678def get_site_configs(cmd, resource_group_name, name, slot=None):
@@ -9931,6 +10016,7 @@ def _poll_deployment_runtime_status(cmd, resource_group_name, webapp_name, slot,
993110016 time_elapsed = 0
993210017 deployment_status = None
993310018 response_body = None
10019+ status_tip_logged = False
993410020 while time_elapsed < max_time_sec:
993510021 try:
993610022 response_body = send_raw_request(cmd.cli_ctx, "GET", deploymentstatusapi_url).json()
@@ -9945,10 +10031,15 @@ def _poll_deployment_runtime_status(cmd, resource_group_name, webapp_name, slot,
994510031 status = deployment_status if status is None else status
994610032 logger.warning("Status: %s Time: %s(s)", status, time_elapsed)
994710033 if deployment_status == "RuntimeStarting":
10034+ if not status_tip_logged:
10035+ _log_webapp_status_tip(webapp_name, resource_group_name)
10036+ status_tip_logged = True
994810037 logger.info("InprogressInstances: %s, SuccessfulInstances: %s",
994910038 deployment_properties.get('numberOfInstancesInProgress'),
995010039 deployment_properties.get('numberOfInstancesSuccessful'))
995110040 if deployment_status == "RuntimeSuccessful":
10041+ if not status_tip_logged:
10042+ _log_webapp_status_tip(webapp_name, resource_group_name)
995210043 break
995310044 if deployment_status == "RuntimeFailed":
995410045 error_text = ""
@@ -10834,6 +10925,8 @@ def webapp_up(cmd, name=None, resource_group_name=None, plan=None, location=None
1083410925 logger.warning("You can launch the app at %s", _url)
1083510926 create_json.update({'URL': _url})
1083610927
10928+ _log_webapp_status_tip(name, rg_name)
10929+
1083710930 if logs:
1083810931 _configure_default_logging(cmd, rg_name, name)
1083910932 try:
@@ -11588,6 +11681,8 @@ def _make_onedeploy_request(params):
1158811681 logger.warning("Deployment status is: \"%s\"", state)
1158911682 response_body = response.json().get("properties", {})
1159011683 logger.warning("Deployment has completed successfully")
11684+ if not (poll_async_deployment_for_debugging and params.track_status):
11685+ _log_webapp_status_tip(params.webapp_name, params.resource_group_name)
1159111686 logger.warning("You can visit your app at: %s", _get_visit_url(params))
1159211687 return response_body
1159311688
0 commit comments