Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/azure-cli/azure/cli/command_modules/appservice/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -1607,6 +1607,9 @@
- name: Set a web app container's settings. (autogenerated)
text: az webapp config container set --docker-custom-image-name MyDockerCustomImage --docker-registry-server-password StrongPassword --docker-registry-server-url https://{azure-container-registry-name}.azurecr.io --docker-registry-server-user DockerUserId --name MyWebApp --resource-group MyResourceGroup
crafted: true
- name: Set a web app container to pull from ACR using a managed identity.
text: az webapp config container set --container-image-name myregistry.azurecr.io/myimage:latest --container-registry-url https://myregistry.azurecr.io --assign-identity [system] --acr-use-identity --acr-identity [system] --name MyWebApp --resource-group MyResourceGroup
crafted: true
"""

helps['webapp config container show'] = """
Expand Down
17 changes: 17 additions & 0 deletions src/azure-cli/azure/cli/command_modules/appservice/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,23 @@ def load_arguments(self, _):
c.ignore('min_replicas')
c.ignore('max_replicas')

with self.argument_context('webapp config container set') as c:
c.argument('assign_identities', nargs='*', options_list=['--assign-identity'],
help="Accept system or user assigned identities separated by spaces. "
"Use '[system]' to refer to system assigned identity, "
"or a resource id to refer to user assigned identity. "
"Check out help for more examples")
c.argument('scope', options_list=['--scope'],
help="Scope that the system assigned identity can access")
c.argument('role', options_list=['--role'],
help="Role name or id the system assigned identity will have")
c.argument('acr_use_identity', arg_type=get_three_state_flag(return_label=True),
help="Enable or disable pulling images from ACR using a managed identity")
c.argument('acr_identity',
help="Accept system or user assigned identity which will be used for ACR image pull. "
"Use '[system]' to refer to system assigned identity, "
"or a resource id to refer to user assigned identity.")

with self.argument_context('functionapp config container') as c:
c.argument('registry_server', options_list=['--registry-server', '-r', c.deprecate(target='--docker-registry-server-url', redirect='--registry-server')],
help='the container registry server url')
Expand Down
110 changes: 84 additions & 26 deletions src/azure-cli/azure/cli/command_modules/appservice/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -3598,47 +3598,105 @@ def _redact_connection_strings(properties):
APPSETTINGS_TO_MASK = ['DOCKER_REGISTRY_SERVER_PASSWORD']


def update_container_settings(cmd, resource_group_name, name, container_registry_url=None,
container_image_name=None, container_registry_user=None,
websites_enable_app_service_storage=None, container_registry_password=None,
multicontainer_config_type=None, multicontainer_config_file=None,
slot=None, min_replicas=None, max_replicas=None):
settings = []
if container_registry_url is not None:
settings.append('DOCKER_REGISTRY_SERVER_URL=' + container_registry_url)
def _is_key_vault_reference(value):
"""Check if a setting value is a Key Vault reference."""
return isinstance(value, str) and value.strip().startswith('@Microsoft.KeyVault(')

if (not container_registry_user and not container_registry_password and
container_registry_url and '.azurecr.io' in container_registry_url):
logger.warning('No credential was provided to access Azure Container Registry. Trying to look up...')
parsed = urlparse(container_registry_url)
registry_name = (parsed.netloc if parsed.scheme else parsed.path).split('.')[0]
try:
container_registry_user, container_registry_password = _get_acr_cred(cmd.cli_ctx, registry_name)
except Exception as ex: # pylint: disable=broad-except
logger.warning("Retrieving credentials failed with an exception:'%s'", ex) # consider throw if needed

def _build_container_updates(container_registry_url, container_registry_user,
container_registry_password, websites_enable_app_service_storage):
"""Build dict of container-specific app settings that were explicitly provided."""
updates = {}
if container_registry_url is not None:
updates['DOCKER_REGISTRY_SERVER_URL'] = container_registry_url
if container_registry_user is not None:
settings.append('DOCKER_REGISTRY_SERVER_USERNAME=' + container_registry_user)
updates['DOCKER_REGISTRY_SERVER_USERNAME'] = container_registry_user
if container_registry_password is not None:
settings.append('DOCKER_REGISTRY_SERVER_PASSWORD=' + container_registry_password)
updates['DOCKER_REGISTRY_SERVER_PASSWORD'] = container_registry_password
if websites_enable_app_service_storage:
settings.append('WEBSITES_ENABLE_APP_SERVICE_STORAGE=' + websites_enable_app_service_storage)
updates['WEBSITES_ENABLE_APP_SERVICE_STORAGE'] = websites_enable_app_service_storage
return updates


if container_registry_user or container_registry_password or container_registry_url or websites_enable_app_service_storage: # pylint: disable=line-too-long
update_app_settings(cmd, resource_group_name, name, settings, slot)
def update_container_settings(cmd, resource_group_name, name, container_registry_url=None,
container_image_name=None, container_registry_user=None,
websites_enable_app_service_storage=None, container_registry_password=None,
multicontainer_config_type=None, multicontainer_config_file=None,
slot=None, min_replicas=None, max_replicas=None,
assign_identities=None, role='AcrPull', scope=None,
acr_use_identity=None, acr_identity=None):
# Read existing app settings so we can preserve non-container settings and Key Vault references
existing_app_settings = _generic_site_operation(cmd.cli_ctx, resource_group_name, name,
'list_application_settings', slot)
existing_properties = existing_app_settings.properties or {}

# Skip credential lookup entirely when managed identity ACR pull is enabled
if acr_use_identity:
logger.info('Managed identity ACR pull is enabled; skipping automatic credential lookup.')
elif (not container_registry_user and not container_registry_password and
container_registry_url and '.azurecr.io' in container_registry_url):
existing_user_val = existing_properties.get('DOCKER_REGISTRY_SERVER_USERNAME', '')
existing_pass_val = existing_properties.get('DOCKER_REGISTRY_SERVER_PASSWORD', '')
if _is_key_vault_reference(existing_user_val) or _is_key_vault_reference(existing_pass_val):
logger.warning('Existing registry credentials use Key Vault references. '
'Skipping automatic credential lookup.')
else:
logger.warning('No credential was provided to access Azure Container Registry. '
'Trying to look up...')
parsed = urlparse(container_registry_url)
registry_name = (parsed.netloc if parsed.scheme else parsed.path).split('.')[0]
try:
container_registry_user, container_registry_password = _get_acr_cred(cmd.cli_ctx,
registry_name)
except Exception as ex: # pylint: disable=broad-except
logger.warning("Retrieving credentials failed with an exception:'%s'", ex)

# Build dict of only the container-specific settings that were explicitly provided
container_updates = _build_container_updates(container_registry_url, container_registry_user,
container_registry_password,
websites_enable_app_service_storage)

if container_updates:
# Merge only container-specific keys into the existing settings,
# preserving all other app settings (including Key Vault references) as-is
for key, value in container_updates.items():
existing_app_settings.properties[key] = value
client = web_client_factory(cmd.cli_ctx)
if is_centauri_functionapp(cmd, resource_group_name, name):
update_application_settings_polling(cmd, resource_group_name, name,
existing_app_settings, slot, client)
else:
_generic_settings_operation(cmd.cli_ctx, resource_group_name, name,
'update_application_settings',
existing_app_settings, slot, client)
settings = get_app_settings(cmd, resource_group_name, name, slot)
if container_image_name is not None:
_add_fx_version(cmd, resource_group_name, name, container_image_name, slot)

# Accumulate site-config changes and issue a single update_site_configs call
site_config_kwargs = {}
if multicontainer_config_file and multicontainer_config_type:
encoded_config_file = _get_linux_multicontainer_encoded_config_from_file(multicontainer_config_file)
linux_fx_version = _format_fx_version(encoded_config_file, multicontainer_config_type)
update_site_configs(cmd, resource_group_name, name, linux_fx_version=linux_fx_version, slot=slot)
site_config_kwargs['linux_fx_version'] = _format_fx_version(encoded_config_file,
multicontainer_config_type)
elif multicontainer_config_file or multicontainer_config_type:
logger.warning('Must change both settings --multicontainer-config-file FILE --multicontainer-config-type TYPE')

if min_replicas is not None or max_replicas is not None:
update_site_configs(cmd, resource_group_name, name, min_replicas=min_replicas, max_replicas=max_replicas)
if min_replicas is not None:
site_config_kwargs['min_replicas'] = min_replicas
if max_replicas is not None:
site_config_kwargs['max_replicas'] = max_replicas

if acr_use_identity is not None:
site_config_kwargs['acr_use_identity'] = acr_use_identity
if acr_identity is not None:
site_config_kwargs['acr_identity'] = acr_identity

if site_config_kwargs:
update_site_configs(cmd, resource_group_name, name, slot=slot, **site_config_kwargs)

if assign_identities is not None:
assign_identity(cmd, resource_group_name, name, assign_identities, role, slot, scope)

return _mask_creds_related_appsettings(_filter_for_container_settings(cmd, resource_group_name, name, settings,
slot=slot))
Expand Down
Loading
Loading