Skip to content

Commit 74daa6c

Browse files
committed
update commands
1 parent f7f6f67 commit 74daa6c

5 files changed

Lines changed: 1110 additions & 506 deletions

File tree

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,24 @@ def load_arguments(self, _):
706706
c.argument('strategy_type', options_list=['--type'], arg_type=get_enum_type(UPDATE_STRATEGY_TYPES),
707707
help="The update strategy type. Allowed values: Recreate, RollingUpdate.")
708708

709+
# Add optional 'name' parameter for functionapp SSL commands to support Flex Consumption apps
710+
with self.argument_context('functionapp config ssl list') as c:
711+
c.argument('name', options_list=['--name', '-n'], help='Name of the function app. Required for Flex Consumption apps to list site-scoped certificates.')
712+
713+
with self.argument_context('functionapp config ssl show') as c:
714+
c.argument('name', options_list=['--name', '-n'], help='Name of the function app. Required for Flex Consumption apps to show site-scoped certificates.')
715+
716+
with self.argument_context('functionapp config ssl delete') as c:
717+
c.argument('name', options_list=['--name', '-n'], help='Name of the function app. Required for Flex Consumption apps to delete site-scoped certificates.')
718+
719+
# Add load_to_code parameter for functionapp SSL commands that create/update certificates (Flex Consumption only)
720+
with self.argument_context('functionapp config ssl upload') as c:
721+
c.argument('load_to_code', arg_type=get_three_state_flag(), help='For Flex Consumption apps only. Make the certificate accessible to app code. When set to true, the certificate can be loaded via the WEBSITE_LOAD_CERTIFICATES app setting.')
722+
723+
with self.argument_context('functionapp config ssl import') as c:
724+
c.argument('load_to_code', arg_type=get_three_state_flag(), help='For Flex Consumption apps only. Make the certificate accessible to app code. When set to true, the certificate can be loaded via the WEBSITE_LOAD_CERTIFICATES app setting.')
725+
c.argument('enable_using_msi', arg_type=get_three_state_flag(), help='For Flex Consumption apps only. Enable Key Vault access using Managed Service Identity. When set to true, the app will use its managed identity to access Key Vault instead of service principal.')
726+
709727
with self.argument_context('webapp config connection-string list') as c:
710728
c.argument('name', arg_type=webapp_name_arg_type, id_part=None)
711729

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

Lines changed: 133 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# --------------------------------------------------------------------------------------------
55

66
import ast
7+
import base64
78
import threading
89
import time
910
import re
@@ -6736,7 +6737,7 @@ def _get_log(url, headers, log_file=None):
67366737
def upload_ssl_cert(cmd, resource_group_name,
67376738
name, certificate_password,
67386739
certificate_file, slot=None,
6739-
certificate_name=None):
6740+
certificate_name=None, load_to_code=None):
67406741
Certificate = cmd.get_models('Certificate')
67416742
client = web_client_factory(cmd.cli_ctx)
67426743
webapp = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get', slot)
@@ -6757,6 +6758,32 @@ def upload_ssl_cert(cmd, resource_group_name,
67576758
webapp.location, resource_group_name)
67586759
cert = Certificate(password=certificate_password, pfx_blob=cert_contents,
67596760
location=webapp.location, server_farm_id=get_site_server_farm_id(webapp))
6761+
6762+
# Check if this is a Flex Consumption function app
6763+
is_flex = is_flex_functionapp(cmd.cli_ctx, resource_group_name, name)
6764+
6765+
# For Flex Consumption apps, use the site-scoped certificates endpoint (slots not supported)
6766+
if is_flex:
6767+
# Build certificate envelope as dict to include loadCertificateToWebsitesSettings
6768+
# (not in SDK model yet)
6769+
cert_envelope = {
6770+
"location": webapp.location,
6771+
"properties": {
6772+
"password": certificate_password,
6773+
"pfxBlob": base64.b64encode(cert_contents).decode('utf-8'),
6774+
"serverFarmId": get_site_server_farm_id(webapp)
6775+
}
6776+
}
6777+
if load_to_code is not None:
6778+
cert_envelope["properties"]["loadCertificateToWebsitesSettings"] = {
6779+
"loadToWebsite": load_to_code
6780+
}
6781+
return client.site_certificates.create_or_update(
6782+
resource_group_name=resource_group_name,
6783+
name=name,
6784+
certificate_name=cert_name,
6785+
certificate_envelope=cert_envelope
6786+
)
67606787
return client.certificates.create_or_update(resource_group_name, cert_name, cert)
67616788

67626789

@@ -6774,26 +6801,52 @@ def _get_cert(certificate_password, certificate_file):
67746801
return thumbprint
67756802

67766803

6777-
def list_ssl_certs(cmd, resource_group_name):
6804+
def list_ssl_certs(cmd, resource_group_name, name=None):
67786805
client = web_client_factory(cmd.cli_ctx)
6806+
6807+
# Check if this is a Flex Consumption function app
6808+
if name and is_flex_functionapp(cmd.cli_ctx, resource_group_name, name):
6809+
return client.site_certificates.list(resource_group_name=resource_group_name, name=name)
67796810
return client.certificates.list_by_resource_group(resource_group_name)
67806811

67816812

6782-
def show_ssl_cert(cmd, resource_group_name, certificate_name):
6813+
def show_ssl_cert(cmd, resource_group_name, certificate_name, name=None):
67836814
client = web_client_factory(cmd.cli_ctx)
6815+
6816+
# Check if this is a Flex Consumption function app
6817+
if name and is_flex_functionapp(cmd.cli_ctx, resource_group_name, name):
6818+
return client.site_certificates.get(
6819+
resource_group_name=resource_group_name,
6820+
name=name,
6821+
certificate_name=certificate_name
6822+
)
67846823
return client.certificates.get(resource_group_name, certificate_name)
67856824

67866825

6787-
def delete_ssl_cert(cmd, resource_group_name, certificate_thumbprint):
6826+
def delete_ssl_cert(cmd, resource_group_name, certificate_thumbprint, name=None):
67886827
client = web_client_factory(cmd.cli_ctx)
6828+
6829+
# Check if this is a Flex Consumption function app
6830+
if name and is_flex_functionapp(cmd.cli_ctx, resource_group_name, name):
6831+
site_certs = client.site_certificates.list(resource_group_name=resource_group_name, name=name)
6832+
for site_cert in site_certs:
6833+
if site_cert.thumbprint == certificate_thumbprint:
6834+
return client.site_certificates.delete(
6835+
resource_group_name=resource_group_name,
6836+
name=name,
6837+
certificate_name=site_cert.name
6838+
)
6839+
raise ResourceNotFoundError("Certificate for thumbprint '{}' not found".format(certificate_thumbprint))
6840+
67896841
webapp_certs = client.certificates.list_by_resource_group(resource_group_name)
67906842
for webapp_cert in webapp_certs:
67916843
if webapp_cert.thumbprint == certificate_thumbprint:
67926844
return client.certificates.delete(resource_group_name, webapp_cert.name)
67936845
raise ResourceNotFoundError("Certificate for thumbprint '{}' not found".format(certificate_thumbprint))
67946846

67956847

6796-
def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_name, name=None, certificate_name=None):
6848+
def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_name, name=None, certificate_name=None,
6849+
load_to_code=None, enable_using_msi=None):
67976850
Certificate = cmd.get_models('Certificate')
67986851
client = web_client_factory(cmd.cli_ctx)
67996852

@@ -6841,17 +6894,20 @@ def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_n
68416894
from azure.cli.core.commands.client_factory import get_subscription_id
68426895
subscription_id = get_subscription_id(cmd.cli_ctx)
68436896
if cloud_type.lower() == PUBLIC_CLOUD.lower():
6897+
# Check if app_service_certificate_orders operation group is available in the SDK
68446898
if kv_subscription.lower() != subscription_id.lower():
68456899
diff_subscription_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_APPSERVICE,
68466900
subscription_id=kv_subscription)
6847-
ascs = diff_subscription_client.app_service_certificate_orders.list(api_version=VERSION_2022_09_01)
6901+
cert_orders_client = diff_subscription_client
68486902
else:
6849-
ascs = client.app_service_certificate_orders.list(api_version=VERSION_2022_09_01)
6903+
cert_orders_client = client
68506904

6851-
kv_secret_name = None
6852-
for asc in ascs:
6853-
if asc.name == key_vault_certificate_name:
6854-
kv_secret_name = asc.certificates[key_vault_certificate_name].key_vault_secret_name
6905+
if hasattr(cert_orders_client, 'app_service_certificate_orders'):
6906+
ascs = cert_orders_client.app_service_certificate_orders.list(api_version=VERSION_2022_09_01)
6907+
for asc in ascs:
6908+
if asc.name == key_vault_certificate_name:
6909+
kv_secret_name = asc.certificates[key_vault_certificate_name].key_vault_secret_name
6910+
break
68556911

68566912
# if kv_secret_name is not populated, it is not an appservice certificate, proceed for KV certificates
68576913
if not kv_secret_name:
@@ -6872,6 +6928,31 @@ def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_n
68726928
kv_cert_def = Certificate(location=location, key_vault_id=kv_id, password='',
68736929
key_vault_secret_name=kv_secret_name)
68746930

6931+
# Check if this is a Flex Consumption function app
6932+
if name and is_flex_functionapp(cmd.cli_ctx, resource_group_name, name):
6933+
# Build certificate envelope as dict to include loadCertificateToWebsitesSettings
6934+
# (not in SDK model yet)
6935+
cert_envelope = {
6936+
"location": location,
6937+
"properties": {
6938+
"keyVaultId": kv_id,
6939+
"password": "",
6940+
"keyVaultSecretName": kv_secret_name
6941+
}
6942+
}
6943+
if load_to_code is not None:
6944+
cert_envelope["properties"]["loadCertificateToWebsitesSettings"] = {
6945+
"loadToWebsite": load_to_code
6946+
}
6947+
if enable_using_msi is not None:
6948+
cert_envelope["properties"]["enableKeyVaultAccessUsingMSI"] = enable_using_msi
6949+
return client.site_certificates.create_or_update(
6950+
resource_group_name=resource_group_name,
6951+
name=name,
6952+
certificate_name=cert_name,
6953+
certificate_envelope=cert_envelope
6954+
)
6955+
68756956
return client.certificates.create_or_update(name=cert_name, resource_group_name=resource_group_name,
68766957
certificate_envelope=kv_cert_def)
68776958

@@ -6903,9 +6984,23 @@ def create_managed_ssl_cert(cmd, resource_group_name, name, hostname, slot=None,
69036984
easy_cert_def = Certificate(location=location, canonical_name=hostname,
69046985
server_farm_id=server_farm_id, password='')
69056986

6987+
# Check if this is a Flex Consumption function app
6988+
is_flex = is_flex_functionapp(cmd.cli_ctx, resource_group_name, name)
6989+
6990+
# Default certificate_name to hostname if not provided
6991+
if not certificate_name:
6992+
certificate_name = hostname
6993+
69066994
# TODO: Update manual polling to use LongRunningOperation once backend API & new SDK supports polling
69076995
try:
6908-
certificate_name = hostname if not certificate_name else certificate_name
6996+
# For Flex Consumption apps, use the site-scoped certificates endpoint (slots not supported)
6997+
if is_flex:
6998+
return client.site_certificates.create_or_update(
6999+
resource_group_name=resource_group_name,
7000+
name=name,
7001+
certificate_name=certificate_name,
7002+
certificate_envelope=easy_cert_def
7003+
)
69097004
return client.certificates.create_or_update(name=certificate_name, resource_group_name=resource_group_name,
69107005
certificate_envelope=easy_cert_def)
69117006
except Exception as ex:
@@ -6973,24 +7068,37 @@ def _update_ssl_binding(cmd, resource_group_name, name, certificate_thumbprint,
69737068
if not webapp:
69747069
raise ResourceNotFoundError("'{}' app doesn't exist".format(name))
69757070

6976-
cert_resource_group_name = parse_resource_id(get_site_server_farm_id(webapp))['resource_group']
6977-
webapp_certs = client.certificates.list_by_resource_group(cert_resource_group_name)
7071+
# Check if this is a Flex Consumption function app
7072+
is_flex = is_flex_functionapp(cmd.cli_ctx, resource_group_name, name)
69787073

69797074
found_cert = None
6980-
# search for a cert that matches in the app service plan's RG
6981-
for webapp_cert in webapp_certs:
6982-
if webapp_cert.thumbprint == certificate_thumbprint:
6983-
found_cert = webapp_cert
6984-
# search for a cert that matches in the webapp's RG
6985-
if not found_cert:
6986-
webapp_certs = client.certificates.list_by_resource_group(resource_group_name)
7075+
7076+
# For Flex Consumption apps, search in site-scoped certificates
7077+
if is_flex:
7078+
site_certs = client.site_certificates.list(resource_group_name=resource_group_name, name=name)
7079+
for site_cert in site_certs:
7080+
if site_cert.thumbprint == certificate_thumbprint:
7081+
found_cert = site_cert
7082+
break
7083+
# If not a Flex app, search in regular certificates
7084+
else:
7085+
cert_resource_group_name = parse_resource_id(get_site_server_farm_id(webapp))['resource_group']
7086+
webapp_certs = client.certificates.list_by_resource_group(cert_resource_group_name)
7087+
# search for a cert that matches in the app service plan's RG
69877088
for webapp_cert in webapp_certs:
69887089
if webapp_cert.thumbprint == certificate_thumbprint:
69897090
found_cert = webapp_cert
6990-
# search for a cert that matches in the subscription, filtering on the serverfarm
6991-
if not found_cert:
6992-
sub_certs = client.certificates.list(filter=f"ServerFarmId eq '{get_site_server_farm_id(webapp)}'")
6993-
found_cert = next(iter([c for c in sub_certs if c.thumbprint == certificate_thumbprint]), None)
7091+
# search for a cert that matches in the webapp's RG
7092+
if not found_cert:
7093+
webapp_certs = client.certificates.list_by_resource_group(resource_group_name)
7094+
for webapp_cert in webapp_certs:
7095+
if webapp_cert.thumbprint == certificate_thumbprint:
7096+
found_cert = webapp_cert
7097+
# search for a cert that matches in the subscription, filtering on the serverfarm
7098+
if not found_cert:
7099+
sub_certs = client.certificates.list(filter=f"ServerFarmId eq '{get_site_server_farm_id(webapp)}'")
7100+
found_cert = next(iter([c for c in sub_certs if c.thumbprint == certificate_thumbprint]), None)
7101+
69947102
if found_cert:
69957103
if not hostname:
69967104
if len(found_cert.host_names) == 1 and not found_cert.host_names[0].startswith('*'):

0 commit comments

Comments
 (0)