Skip to content

Commit ed3032d

Browse files
committed
quick fixes
1 parent 2aa7e0d commit ed3032d

2 files changed

Lines changed: 96 additions & 9 deletions

File tree

src/azure-cli/azure/cli/command_modules/appservice/custom.py

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6785,6 +6785,13 @@ def upload_ssl_cert(cmd, resource_group_name,
67856785
# Check if this is a Flex Consumption function app
67866786
is_flex = is_flex_functionapp(cmd.cli_ctx, resource_group_name, name)
67876787

6788+
# Validate parameter usage
6789+
if is_flex and slot:
6790+
raise ArgumentUsageError("--slot is not supported for Flex Consumption function apps. "
6791+
"Site-scoped certificates apply to the parent app.")
6792+
if load_to_code is not None and not is_flex:
6793+
raise ArgumentUsageError("--load-to-code is only supported for Flex Consumption function apps.")
6794+
67886795
# For Flex Consumption apps, use the site-scoped certificates endpoint (slots not supported)
67896796
if is_flex:
67906797
# Build certificate envelope as dict to include loadCertificateToWebsitesSettings
@@ -6830,6 +6837,9 @@ def list_ssl_certs(cmd, resource_group_name, name=None):
68306837
# Check if this is a Flex Consumption function app
68316838
if name and is_flex_functionapp(cmd.cli_ctx, resource_group_name, name):
68326839
return client.site_certificates.list(resource_group_name=resource_group_name, name=name)
6840+
if name:
6841+
raise ArgumentUsageError("--name is only supported for Flex Consumption function apps. "
6842+
"For other app types, certificates are managed at the resource group level.")
68336843
return client.certificates.list_by_resource_group(resource_group_name)
68346844

68356845

@@ -6843,6 +6853,9 @@ def show_ssl_cert(cmd, resource_group_name, certificate_name, name=None):
68436853
name=name,
68446854
certificate_name=certificate_name
68456855
)
6856+
if name:
6857+
raise ArgumentUsageError("--name is only supported for Flex Consumption function apps. "
6858+
"For other app types, certificates are managed at the resource group level.")
68466859
return client.certificates.get(resource_group_name, certificate_name)
68476860

68486861

@@ -6861,15 +6874,30 @@ def delete_ssl_cert(cmd, resource_group_name, certificate_thumbprint, name=None)
68616874
)
68626875
raise ResourceNotFoundError("Certificate for thumbprint '{}' not found".format(certificate_thumbprint))
68636876

6877+
if name:
6878+
raise ArgumentUsageError("--name is only supported for Flex Consumption function apps. "
6879+
"For other app types, certificates are managed at the resource group level.")
68646880
webapp_certs = client.certificates.list_by_resource_group(resource_group_name)
68656881
for webapp_cert in webapp_certs:
68666882
if webapp_cert.thumbprint == certificate_thumbprint:
68676883
return client.certificates.delete(resource_group_name, webapp_cert.name)
68686884
raise ResourceNotFoundError("Certificate for thumbprint '{}' not found".format(certificate_thumbprint))
68696885

68706886

6887+
def _validate_flex_ssl_params(is_flex, load_to_code, enable_using_msi, webapp, resource_group_name, name):
6888+
"""Validate SSL import parameters for Flex Consumption apps."""
6889+
if load_to_code is not None and not is_flex:
6890+
raise ArgumentUsageError("--load-to-code is only supported for Flex Consumption function apps.")
6891+
if enable_using_msi is not None and not is_flex:
6892+
raise ArgumentUsageError("--enable-using-msi is only supported for Flex Consumption function apps.")
6893+
if enable_using_msi and (not webapp.identity or not webapp.identity.type):
6894+
raise ArgumentUsageError(
6895+
"--enable-using-msi requires a managed identity assigned to the function app. "
6896+
"Assign one with: az functionapp identity assign -g {} -n {}".format(resource_group_name, name))
6897+
6898+
68716899
def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_name, name=None, certificate_name=None,
6872-
load_to_code=None, enable_using_msi=None): # pylint: disable=too-many-branches
6900+
load_to_code=None, enable_using_msi=None):
68736901
Certificate = cmd.get_models('Certificate')
68746902
client = web_client_factory(cmd.cli_ctx)
68756903

@@ -6931,33 +6959,49 @@ def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_n
69316959
if asc.name == key_vault_certificate_name:
69326960
kv_secret_name = asc.certificates[key_vault_certificate_name].key_vault_secret_name
69336961
break
6962+
else:
6963+
logger.warning("Unable to check App Service Certificate orders. "
6964+
"If '%s' is an App Service Certificate, the import may not resolve "
6965+
"the correct Key Vault secret name.", key_vault_certificate_name)
69346966

69356967
# if kv_secret_name is not populated, it is not an appservice certificate, proceed for KV certificates
69366968
if not kv_secret_name:
69376969
kv_secret_name = key_vault_certificate_name
69386970

69396971
cert_name = certificate_name or '{}-{}-{}'.format(resource_group_name, kv_name, key_vault_certificate_name)
69406972

6941-
lnk = 'https://azure.github.io/AppService/2016/05/24/Deploying-Azure-Web-App-Certificate-through-Key-Vault.html'
6942-
lnk_msg = 'Find more details here: {}'.format(lnk)
6943-
if not _check_service_principal_permissions(cmd, kv_resource_group_name, kv_name, kv_subscription):
6944-
logger.warning('Unable to verify Key Vault permissions.')
6945-
logger.warning('You may need to grant Microsoft.Azure.WebSites service principal the Secret:Get permission')
6946-
logger.warning(lnk_msg)
6973+
# When using MSI, the app's managed identity accesses Key Vault, not the service principal
6974+
if enable_using_msi:
6975+
logger.warning('Using managed identity to access Key Vault. '
6976+
'Ensure the app\'s managed identity has the permission on the Key Vault.')
6977+
else:
6978+
lnk = 'https://azure.github.io/AppService/2016/05/24/Deploying-Azure-Web-App-Certificate-through-Key-Vault.html'
6979+
lnk_msg = 'Find more details here: {}'.format(lnk)
6980+
if not _check_service_principal_permissions(cmd, kv_resource_group_name, kv_name, kv_subscription):
6981+
logger.warning('Unable to verify Key Vault permissions.')
6982+
logger.warning('You may need to grant Microsoft.Azure.WebSites service principal the Secret:Get permission')
6983+
logger.warning(lnk_msg)
69476984

69486985
kv_cert_def = Certificate(location=location, key_vault_id=kv_id, password='',
69496986
key_vault_secret_name=kv_secret_name)
69506987

69516988
# Check if this is a Flex Consumption function app
6952-
if name and is_flex_functionapp(cmd.cli_ctx, resource_group_name, name):
6989+
is_flex = name and is_flex_functionapp(cmd.cli_ctx, resource_group_name, name)
6990+
6991+
# Validate parameter usage
6992+
_validate_flex_ssl_params(is_flex, load_to_code, enable_using_msi, webapp if name else None,
6993+
resource_group_name, name)
6994+
6995+
if is_flex:
69536996
# Build certificate envelope as dict to include loadCertificateToWebsitesSettings
69546997
# (not in SDK model yet)
69556998
cert_envelope = {
69566999
"location": location,
69577000
"properties": {
69587001
"keyVaultId": kv_id,
69597002
"password": "",
6960-
"keyVaultSecretName": kv_secret_name
7003+
"keyVaultSecretName": kv_secret_name,
7004+
"serverFarmId": get_site_server_farm_id(webapp)
69617005
}
69627006
}
69637007
if load_to_code is not None:
@@ -7007,6 +7051,11 @@ def create_managed_ssl_cert(cmd, resource_group_name, name, hostname, slot=None,
70077051
# Check if this is a Flex Consumption function app
70087052
is_flex = is_flex_functionapp(cmd.cli_ctx, resource_group_name, name)
70097053

7054+
# Validate parameter usage
7055+
if is_flex and slot:
7056+
raise ArgumentUsageError("--slot is not supported for Flex Consumption function apps. "
7057+
"Site-scoped certificates apply to the parent app.")
7058+
70107059
# Default certificate_name to hostname if not provided
70117060
if not certificate_name:
70127061
certificate_name = hostname
@@ -7091,6 +7140,11 @@ def _update_ssl_binding(cmd, resource_group_name, name, certificate_thumbprint,
70917140
# Check if this is a Flex Consumption function app
70927141
is_flex = is_flex_functionapp(cmd.cli_ctx, resource_group_name, name)
70937142

7143+
# Validate parameter usage for Flex apps
7144+
if is_flex and slot:
7145+
raise ArgumentUsageError("--slot is not supported for Flex Consumption function apps. "
7146+
"Site-scoped certificates apply to the parent app.")
7147+
70947148
found_cert = None
70957149

70967150
# For Flex Consumption apps, search in site-scoped certificates
@@ -7121,6 +7175,12 @@ def _update_ssl_binding(cmd, resource_group_name, name, certificate_thumbprint,
71217175

71227176
if found_cert:
71237177
if not hostname:
7178+
# For Flex Consumption apps, site-scoped certificates may not populate host_names
7179+
# Require --hostname to be specified explicitly in this case
7180+
if is_flex and (not found_cert.host_names or len(found_cert.host_names) == 0):
7181+
raise ArgumentUsageError(
7182+
"The site-scoped certificate does not have associated host names. "
7183+
"Please specify the hostname explicitly using --hostname.")
71247184
if len(found_cert.host_names) == 1 and not found_cert.host_names[0].startswith('*'):
71257185
return _update_host_name_ssl_state(cmd, resource_group_name, name, webapp,
71267186
found_cert.host_names[0], ssl_type,

src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_functionapp_commands.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,33 @@ def test_functionapp_flex_ssl_bind_unbind(self, resource_group, storage_account)
17371737
self.cmd('functionapp config ssl delete -g {} -n {} --certificate-thumbprint {}'
17381738
.format(resource_group, functionapp_name, cert_thumbprint))
17391739

1740+
@ResourceGroupPreparer(location=FLEX_ASP_LOCATION_FUNCTIONAPP)
1741+
@StorageAccountPreparer()
1742+
def test_functionapp_flex_managed_ssl_cert_create(self, resource_group, storage_account):
1743+
"""Test create_managed_ssl_cert on Flex Consumption function apps.
1744+
1745+
This test validates the Flex-specific path in create_managed_ssl_cert which uses
1746+
site_certificates.create_or_update instead of certificates.create_or_update.
1747+
"""
1748+
functionapp_name = self.create_random_name('flexsslcreate', 40)
1749+
test_hostname = 'test.customdomain.com'
1750+
cert_name = 'test-managed-cert'
1751+
1752+
# Create a Flex Consumption function app
1753+
self.cmd('functionapp create -g {} -n {} -f {} -s {} --runtime python --runtime-version 3.11'
1754+
.format(resource_group, functionapp_name, FLEX_ASP_LOCATION_FUNCTIONAPP, storage_account))
1755+
1756+
# Test that --slot is rejected for Flex Consumption apps
1757+
self.cmd('functionapp config ssl create -g {} -n {} --hostname {} --slot staging'
1758+
.format(resource_group, functionapp_name, test_hostname),
1759+
expect_failure=True)
1760+
1761+
# Test that managed certificate creation fails with validation error when hostname is not registered
1762+
# This validates the Flex path is being taken (site_certificates endpoint)
1763+
self.cmd('functionapp config ssl create -g {} -n {} --hostname {} --certificate-name {}'
1764+
.format(resource_group, functionapp_name, test_hostname, cert_name),
1765+
expect_failure=True)
1766+
17401767

17411768
class FunctionAppManagedEnvironment(ScenarioTest):
17421769
@AllowLargeResponse()

0 commit comments

Comments
 (0)