@@ -1353,6 +1353,157 @@ def get_webapp_sitecontainer_log(cmd, name, resource_group, container_name, slot
13531353 raise AzureInternalError ("Failed to fetch sitecontainer logs. Error: {}" .format (str (ex )))
13541354
13551355
1356+ def convert_webapp_sitecontainers (cmd , name , resource_group , mode , slot = None ):
1357+ """
1358+ Convert a webapp between classic and sitecontainers mode.
1359+
1360+ :param cmd: CLI command context
1361+ :param name: Name of the webapp
1362+ :param resource_group: Resource group of the webapp
1363+ :param mode: Target mode, either 'classic' or 'sitecontainers'
1364+ :param slot: Optional deployment slot
1365+ """
1366+ client = web_client_factory (cmd .cli_ctx )
1367+ if mode not in ['classic' , 'sitecontainers' ]:
1368+ raise InvalidArgumentValueError (
1369+ "Invalid mode '{}'. Allowed values: classic, sitecontainers." .format (mode )
1370+ )
1371+
1372+ site_config = get_site_configs (cmd , resource_group , name , slot )
1373+ linux_fx_version = getattr (site_config , "linux_fx_version" , None )
1374+
1375+ if mode == 'sitecontainers' :
1376+ if linux_fx_version and not linux_fx_version .startswith ('DOCKER|' ):
1377+ raise ValidationError ("Cannot convert to sitecontainers mode as site is not a classic custom container app." )
1378+ acr_use_managed_identity_creds = getattr (site_config , "acr_use_managed_identity_creds" , None )
1379+ acr_user_managed_identity_id = getattr (site_config , "acr_user_managed_identity_id" , None )
1380+
1381+ acr_user_name = None
1382+ acr_user_password = None
1383+
1384+ from azure .cli .core .commands .client_factory import get_subscription_id
1385+ subscription_id = get_subscription_id (cmd .cli_ctx )
1386+ url = (
1387+ f"/subscriptions/{ subscription_id } /resourceGroups/{ resource_group } /"
1388+ f"providers/Microsoft.Web/sites/{ name } /config/appsettings/list?api-version=2023-12-01"
1389+ )
1390+ request_url = cmd .cli_ctx .cloud .endpoints .resource_manager + url
1391+ response = send_raw_request (cmd .cli_ctx , "POST" , request_url )
1392+ app_settings_raw = response .json ()
1393+ app_settings = app_settings_raw .get ("properties" , {})
1394+
1395+ acr_user_password = app_settings .get ("DOCKER_REGISTRY_SERVER_PASSWORD" , None )
1396+ acr_user_name = app_settings .get ("DOCKER_REGISTRY_SERVER_USERNAME" , None )
1397+ websites_port = app_settings .get ("WEBSITES_PORT" , None ) or app_settings .get ("PORT" , None )
1398+
1399+ # Get docker image from linux_fx_version
1400+ docker_image = linux_fx_version .replace ("DOCKER|" , "" , 1 )
1401+ sidecarmainname = "main"
1402+ startup_cmd = getattr (site_config , "app_command_line" , None )
1403+
1404+ # Prepare parameters for sitecontainers create
1405+ sitecontainer_kwargs = {
1406+ "name" : name ,
1407+ "resource_group" : resource_group ,
1408+ "slot" : slot ,
1409+ "container_name" : sidecarmainname ,
1410+ "image" : docker_image ,
1411+ "target_port" : websites_port ,
1412+ "startup_cmd" : startup_cmd ,
1413+ "is_main" : True ,
1414+ }
1415+
1416+ if acr_use_managed_identity_creds :
1417+ if acr_user_managed_identity_id :
1418+ # ACR with User-Assigned Managed Identity
1419+ logger .warning ("Site is using User-Assigned Managed Identity for ACR authentication." )
1420+ sitecontainer_kwargs ["user_assigned_identity" ] = acr_user_managed_identity_id
1421+ else :
1422+ # ACR with System-Assigned Managed Identity
1423+ logger .warning ("Site is using System-Assigned Managed Identity for ACR authentication." )
1424+ sitecontainer_kwargs ["system_assigned_identity" ] = True
1425+ else :
1426+ if acr_user_name and acr_user_password :
1427+ # ACR with User Credentials
1428+ logger .warning ("Site is using User Credentials for ACR authentication." )
1429+ sitecontainer_kwargs ["registry_username" ] = acr_user_name
1430+ sitecontainer_kwargs ["registry_password" ] = acr_user_password
1431+ else :
1432+ # No ACR authentication, using anonymous access
1433+ logger .warning ("Site is using anonymous access for ACR authentication." )
1434+ response = create_webapp_sitecontainers (cmd , ** sitecontainer_kwargs )
1435+ if response is None :
1436+ raise AzureInternalError ("Failed to create sitecontainer for conversion to sitecontainers mode." )
1437+
1438+ # Set linuxFxVersion to SITECONTAINERS
1439+ logger .warning ("Setting linuxFxVersion to SITECONTAINERS" )
1440+ update_site_configs (cmd , resource_group , name , slot = slot , linux_fx_version = "SITECONTAINERS" )
1441+ logger .warning ("Webapp '%s' converted to sitecontainers mode." , name )
1442+ return {"result" : "success" , "mode" : mode }
1443+ else : # mode == 'classic'
1444+ if linux_fx_version and not linux_fx_version .lower ().startswith ('sitecontainers' ):
1445+ raise ValidationError ("Cannot convert to classic mode as site is not a SITECONTAINERS app." )
1446+
1447+ # Get the main sitecontainer
1448+ sitecontainers = list_webapp_sitecontainers (cmd , name , resource_group , slot )
1449+ main_container = next ((c for c in sitecontainers if getattr (c , "is_main" , False )), None )
1450+ if not main_container :
1451+ raise ResourceNotFoundError ("No main sitecontainer found. Cannot convert to classic mode." )
1452+
1453+ main_container = update_webapp_sitecontainer (cmd , name , resource_group , main_container .name , slot = slot ,
1454+ image = main_container .image , target_port = main_container .target_port , is_main = main_container .is_main )
1455+
1456+ # Prepare new linux_fx_version
1457+ docker_image = getattr (main_container , "image" , None )
1458+ if not docker_image :
1459+ raise ValidationError ("Main sitecontainer does not have an image specified." )
1460+
1461+ linux_fx_version = _format_fx_version (docker_image )
1462+
1463+ # Prepare app settings for registry credentials if needed
1464+ settings = []
1465+ if main_container .auth_type == AuthType .USER_CREDENTIALS :
1466+ if main_container .user_name :
1467+ settings .append (f"DOCKER_REGISTRY_SERVER_USERNAME={ main_container .user_name } " )
1468+ if main_container .password_secret :
1469+ settings .append (f"DOCKER_REGISTRY_SERVER_PASSWORD={ main_container .password_secret } " )
1470+ if main_container .target_port :
1471+ settings .append (f"WEBSITES_PORT={ main_container .target_port } " )
1472+ elif main_container .auth_type == AuthType .SYSTEM_IDENTITY :
1473+ configs = get_site_configs (cmd , resource_group , name , slot )
1474+ setattr (configs , 'acr_use_managed_identity_creds' , True )
1475+ setattr (configs , 'acr_user_managed_identity_id' , "" )
1476+ _generic_site_operation (cmd .cli_ctx , resource_group , name , 'update_configuration' , slot , configs )
1477+ elif main_container .auth_type == AuthType .USER_ASSIGNED :
1478+ app = client .web_apps .get_slot (resource_group , name , slot ) if slot else client .web_apps .get (resource_group , name )
1479+ if app .identity and app .identity .user_assigned_identities :
1480+ # Find the managed identity key whose client_id matches main_container.user_managed_identity_client_id
1481+ matched_key = None
1482+ for key , identity in app .identity .user_assigned_identities .items ():
1483+ if identity .client_id == main_container .user_managed_identity_client_id :
1484+ matched_key = key
1485+ break
1486+ if not matched_key :
1487+ raise ResourceNotFoundError (
1488+ f"Could not find a user-assigned identity with client_id '{ main_container .user_managed_identity_client_id } ' assigned to the app."
1489+ )
1490+ update_site_configs (cmd , resource_group , name , slot = slot , acr_identity = matched_key )
1491+ elif main_container .auth_type == AuthType .Anonymous :
1492+ update_site_configs (cmd , resource_group , name , slot = slot , acr_use_identity = False )
1493+
1494+ logger .warning ("Deleting all sitecontainers before converting to classic mode." )
1495+ # Remove all sitecontainers
1496+ for c in sitecontainers :
1497+ delete_webapp_sitecontainer (cmd , name , resource_group , c .name , slot )
1498+
1499+ # Set linuxFxVersion to classic custom container
1500+ update_site_configs (cmd , resource_group , name , slot = slot , linux_fx_version = linux_fx_version )
1501+ if settings :
1502+ update_app_settings (cmd , resource_group , name , settings , slot )
1503+ logger .warning ("Webapp '%s' converted to classic custom container mode." , name )
1504+ return {"result" : "success" , "mode" : mode }
1505+
1506+
13561507# for generic updater
13571508def get_webapp (cmd , resource_group_name , name , slot = None ):
13581509 return _generic_site_operation (cmd .cli_ctx , resource_group_name , name , 'get' , slot )
@@ -2181,7 +2332,7 @@ def update_site_configs(cmd, resource_group_name, name, slot=None, number_of_wor
21812332 _update_webapp_current_stack_property_if_needed (cmd , resource_group_name , name , current_stack )
21822333
21832334 if linux_fx_version :
2184- if linux_fx_version .strip ().lower ().startswith ('docker|' ):
2335+ if linux_fx_version .strip ().lower ().startswith ('docker|' ) or linux_fx_version . strip (). lower (). startswith ( 'sitecontainers' ) :
21852336 if ('WEBSITES_ENABLE_APP_SERVICE_STORAGE' not in app_settings .properties or
21862337 app_settings .properties ['WEBSITES_ENABLE_APP_SERVICE_STORAGE' ] != 'true' ):
21872338 update_app_settings (cmd , resource_group_name , name , ["WEBSITES_ENABLE_APP_SERVICE_STORAGE=false" ])
0 commit comments