Skip to content

Commit c25c29d

Browse files
authored
[App Service] Fix #20983: az webapp config ssl import: Make web app a non-required parameter (#30958)
1 parent fed76b3 commit c25c29d

File tree

5 files changed

+147
-11
lines changed

5 files changed

+147
-11
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,10 +1537,12 @@
15371537
type: command
15381538
short-summary: Import an SSL or App Service Certificate to a web app from Key Vault.
15391539
examples:
1540-
- name: Import an SSL or App Service Certificate certificate to a web app from Key Vault.
1540+
- name: Import an SSL or App Service Certificate certificate to a web app from Key Vault. Note that all webapps in the webspace will also be able to use the certificate.
15411541
text: az webapp config ssl import --resource-group MyResourceGroup --name MyWebapp --key-vault MyKeyVault --key-vault-certificate-name MyCertificateName
1542-
- name: Import an SSL or App Service Certificate to a web app from Key Vault using resource id (typically if Key Vault is in another subscription).
1542+
- name: Import an SSL or App Service Certificate to a web app from Key Vault using resource id (typically if Key Vault is in another subscription). Note that all webapps in the webspace will also be able to use the certificate.
15431543
text: az webapp config ssl import --resource-group MyResourceGroup --name MyWebapp --key-vault '/subscriptions/[sub id]/resourceGroups/[rg]/providers/Microsoft.KeyVault/vaults/[vault name]' --key-vault-certificate-name MyCertificateName
1544+
- name: Import an SSL or App Service Certificate certificate to a webspace from Key Vault. Note that all webapps in the webspace will also be able to use the certificate.
1545+
text: az webapp config ssl import --resource-group MyResourceGroup --key-vault MyKeyVault --key-vault-certificate-name MyCertificateName
15441546
"""
15451547

15461548
helps['webapp config ssl create'] = """

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ def load_arguments(self, _):
327327
with self.argument_context(scope + ' config ssl import') as c:
328328
c.argument('key_vault', help='The name or resource ID of the Key Vault')
329329
c.argument('key_vault_certificate_name', help='The name of the certificate in Key Vault')
330+
c.argument('name', help='Name of the web app. This is used to set the location of the webspace for the certificate import. If not specified, the location of the resource group will be used. If you have apps in multiple regions/webspaces, you must specify the name of the app to set the location of the webspace for the certificate import.')
330331
with self.argument_context(scope + ' config ssl create') as c:
331332
c.argument('hostname', help='The custom domain name')
332333
c.argument('name', options_list=['--name', '-n'], help='Name of the web app.')

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3975,14 +3975,22 @@ def delete_ssl_cert(cmd, resource_group_name, certificate_thumbprint):
39753975
raise ResourceNotFoundError("Certificate for thumbprint '{}' not found".format(certificate_thumbprint))
39763976

39773977

3978-
def import_ssl_cert(cmd, resource_group_name, name, key_vault, key_vault_certificate_name, certificate_name=None):
3978+
def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_name, name=None, certificate_name=None):
39793979
Certificate = cmd.get_models('Certificate')
39803980
client = web_client_factory(cmd.cli_ctx)
3981-
webapp = client.web_apps.get(resource_group_name, name)
3982-
if not webapp:
3983-
raise ResourceNotFoundError("'{}' app doesn't exist in resource group {}".format(name, resource_group_name))
3984-
server_farm_id = webapp.server_farm_id
3985-
location = webapp.location
3981+
3982+
# Webapp name is not required for this command, but the location of the webspace is required since the certificate
3983+
# is associated with the webspace, not the app. All apps and plans in the same webspace will share the same
3984+
# certificates. If the app is not provided, the location of the resource group is used.
3985+
if name:
3986+
webapp = client.web_apps.get(resource_group_name, name)
3987+
if not webapp:
3988+
raise ResourceNotFoundError("'{}' app doesn't exist in resource group {}".format(name, resource_group_name))
3989+
location = webapp.location
3990+
else:
3991+
rg_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES)
3992+
location = rg_client.resource_groups.get(resource_group_name).location
3993+
39863994
kv_id = None
39873995
if not is_valid_resource_id(key_vault):
39883996
kv_client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_KEYVAULT)
@@ -3997,9 +4005,9 @@ def import_ssl_cert(cmd, resource_group_name, name, key_vault, key_vault_certifi
39974005
if kv_id is None:
39984006
kv_msg = 'The Key Vault {0} was not found in the subscription in context. ' \
39994007
'If your Key Vault is in a different subscription, please specify the full Resource ID: ' \
4000-
'\naz .. ssl import -n {1} -g {2} --key-vault-certificate-name {3} ' \
4008+
'\naz .. ssl import -g {1} --key-vault-certificate-name {2} ' \
40014009
'--key-vault /subscriptions/[sub id]/resourceGroups/[rg]/providers/Microsoft.KeyVault/' \
4002-
'vaults/{0}'.format(key_vault, name, resource_group_name, key_vault_certificate_name)
4010+
'vaults/{0}'.format(key_vault, resource_group_name, key_vault_certificate_name)
40034011
logger.warning(kv_msg)
40044012
return
40054013

@@ -4044,7 +4052,7 @@ def import_ssl_cert(cmd, resource_group_name, name, key_vault, key_vault_certifi
40444052
logger.warning(lnk_msg)
40454053

40464054
kv_cert_def = Certificate(location=location, key_vault_id=kv_id, password='',
4047-
key_vault_secret_name=kv_secret_name, server_farm_id=server_farm_id)
4055+
key_vault_secret_name=kv_secret_name)
40484056

40494057
return client.certificates.create_or_update(name=cert_name, resource_group_name=resource_group_name,
40504058
certificate_envelope=kv_cert_def)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- '*/*'
7+
Accept-Encoding:
8+
- gzip, deflate
9+
CommandName:
10+
- keyvault set-policy
11+
Connection:
12+
- keep-alive
13+
ParameterSetName:
14+
- -g --name --spn --secret-permissions
15+
User-Agent:
16+
- python/3.12.9 (Windows-11-10.0.26100-SP0) AZURECLI/2.70.0
17+
method: GET
18+
uri: https://graph.microsoft.com/v1.0/servicePrincipals?$filter=servicePrincipalNames%2Fany%28c%3Ac%20eq%20%27Microsoft.Azure.WebSites%27%29
19+
response:
20+
body:
21+
string: '{"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#servicePrincipals","value":[{"id":"f8daea97-62e7-4026-becf-13c2ea98e8b4","deletedDateTime":null,"accountEnabled":true,"alternativeNames":[],"appDisplayName":"Microsoft
22+
Azure App Service","appDescription":null,"appId":"abfa0a7c-a6b6-4736-8310-5855508787cd","applicationTemplateId":null,"appOwnerOrganizationId":"f8cdef31-a31e-4b4a-93e4-5f571e91255a","appRoleAssignmentRequired":false,"createdDateTime":null,"description":null,"disabledByMicrosoftStatus":null,"displayName":"Microsoft
23+
Azure App Service","homepage":null,"loginUrl":null,"logoutUrl":null,"notes":null,"notificationEmailAddresses":[],"preferredSingleSignOnMode":null,"preferredTokenSigningKeyThumbprint":null,"replyUrls":["https://msftintch1.sso.azurewebsites.windows.net/","https://jinc.sso.azurewebsites.windows.net/","https://jinw.sso.azurewebsites.windows.net/","https://sec.sso.azurewebsites.windows.net/","https://qac.sso.azurewebsites.windows.net/","https://ses.sso.azurewebsites.windows.net/","https://plc.sso.azurewebsites.windows.net/","https://itn.sso.azurewebsites.windows.net/","https://ilc.sso.azurewebsites.windows.net/","https://esc.sso.azurewebsites.windows.net/","https://mxc.sso.azurewebsites.windows.net/","https://twn.sso.azurewebsites.windows.net/","https://twnw.sso.azurewebsites.windows.net/","https://hel.sso.azurewebsites.windows.net/","https://ate.sso.azurewebsites.windows.net/","https://kul.sso.azurewebsites.windows.net/","https://lo.sso.azurewebsites.windows.net/","https://cnn10.sso.azurewebsites.windows.net/","https://krs2.sso.azurewebsites.windows.net/","https://brne.sso.azurewebsites.windows.net/","https://clc.sso.azurewebsites.windows.net/","https://clnc.sso.azurewebsites.windows.net/","https://myw.sso.azurewebsites.windows.net/","https://nzn.sso.azurewebsites.windows.net/","https://bec.sso.azurewebsites.windows.net/","https://mys.sso.azurewebsites.windows.net/","https://insc.sso.azurewebsites.windows.net/","https://idc.sso.azurewebsites.windows.net/","https://flc.sso.azurewebsites.windows.net/","https://ilnw.sso.azurewebsites.windows.net/","https://dke.sso.azurewebsites.windows.net/","https://usw3.sso.azurewebsites.windows.net/","https://dxb.sso.azurewebsites.windows.net/","https://brse.sso.azurewebsites.windows.net/","https://chw.sso.azurewebsites.windows.net/","https://trydiagnosticsmesh.azure.com/","https://zrh.sso.azurewebsites.windows.net/","https://yt1.sso.azurewebsites.windows.net/","https://yq1.sso.azurewebsites.windows.net/","https://sy3.sso.azurewebsites.windows.net/","https://svg.sso.azurewebsites.windows.net/","https://sn1.sso.azurewebsites.windows.net/","https://sg1.sso.azurewebsites.windows.net/","https://se1.sso.azurewebsites.windows.net/","https://ps1.sso.azurewebsites.windows.net/","https://pn1.sso.azurewebsites.windows.net/","https://par.sso.azurewebsites.windows.net/","https://osl.sso.azurewebsites.windows.net/","https://os1.sso.azurewebsites.windows.net/","https://mwh.sso.azurewebsites.windows.net/","https://msftintsg1.sso.azurewebsites.windows.net/","https://msftintdm3.sso.azurewebsites.windows.net/","https://mrs.sso.azurewebsites.windows.net/","https://ml1.sso.azurewebsites.windows.net/","https://ma1.sso.azurewebsites.windows.net/","https://ln1.sso.azurewebsites.windows.net/","https://kw1.sso.azurewebsites.windows.net/","https://jnb21.sso.azurewebsites.windows.net/","https://hk1.sso.azurewebsites.windows.net/","https://fra.sso.azurewebsites.windows.net/","https://dm1.sso.azurewebsites.windows.net/","https://db3.sso.azurewebsites.windows.net/","https://cy4.sso.azurewebsites.windows.net/","https://cw1.sso.azurewebsites.windows.net/","https://cq1.sso.azurewebsites.windows.net/","https://cpt20.sso.azurewebsites.windows.net/","https://ch1.sso.azurewebsites.windows.net/","https://cbr21.sso.azurewebsites.windows.net/","https://cbr20.sso.azurewebsites.windows.net/","https://bn1.sso.azurewebsites.windows.net/","https://bm1.sso.azurewebsites.windows.net/","https://blu.sso.azurewebsites.windows.net/","https://ber.sso.azurewebsites.windows.net/","https://bay.sso.azurewebsites.windows.net/","https://auh.sso.azurewebsites.windows.net/","https://am2.sso.azurewebsites.windows.net/","https://euapdm1.sso.azurewebsites.windows.net/","https://euapbn1.sso.azurewebsites.windows.net/","https://msftinthk1.sso.azurewebsites.windows.net/","https://deploy-staging.azure.com","https://functions.azure.com","https://functions-staging.azure.com","https://functions-next.azure.com","https://functions-release.azure.com"],"servicePrincipalNames":["https://appservice.azure.com","Microsoft.Azure.WebSites","abfa0a7c-a6b6-4736-8310-5855508787cd"],"servicePrincipalType":"Application","signInAudience":"AzureADMultipleOrgs","tags":["disableAcceptingTenantedPassthroughTokens","disableRequestingTenantedPassthroughTokens","disableLegacyUserImpersonationResource","disableLegacyUserImpersonationClient"],"tokenEncryptionKeyId":null,"samlSingleSignOnSettings":null,"addIns":[],"appRoles":[],"info":{"logoUrl":null,"marketingUrl":null,"privacyStatementUrl":null,"supportUrl":null,"termsOfServiceUrl":null},"keyCredentials":[],"oauth2PermissionScopes":[{"adminConsentDescription":"Allow
24+
the application to access all the APIs registered with App Service","adminConsentDisplayName":"Access
25+
APIs registered with App Service","id":"e0ea806b-d128-49dc-ac08-2bf18f7874d8","isEnabled":true,"type":"User","userConsentDescription":"Allow
26+
the application to access all the APIs registered with App Service","userConsentDisplayName":"Access
27+
APIs registered with App Service","value":"user_impersonation"}],"passwordCredentials":[],"resourceSpecificApplicationPermissions":[],"verifiedPublisher":{"displayName":null,"verifiedPublisherId":null,"addedDateTime":null}}]}'
28+
headers:
29+
cache-control:
30+
- no-cache
31+
content-length:
32+
- '5754'
33+
content-type:
34+
- application/json; odata.metadata=minimal; odata.streaming=true; IEEE754Compatible=false;
35+
charset=utf-8
36+
date:
37+
- Wed, 05 Mar 2025 21:00:51 GMT
38+
odata-version:
39+
- '4.0'
40+
request-id:
41+
- 1c1c2816-f859-44c9-a943-29e634ead8a7
42+
strict-transport-security:
43+
- max-age=31536000
44+
transfer-encoding:
45+
- chunked
46+
vary:
47+
- Accept-Encoding
48+
x-ms-ags-diagnostic:
49+
- '{"ServerInfo":{"DataCenter":"East US 2","Slice":"E","Ring":"5","ScaleUnit":"002","RoleInstance":"BN2PEPF00003EA9"}}'
50+
x-ms-resource-unit:
51+
- '1'
52+
status:
53+
code: 200
54+
message: OK
55+
- request:
56+
body: null
57+
headers:
58+
Accept:
59+
- application/json
60+
Accept-Encoding:
61+
- gzip, deflate
62+
CommandName:
63+
- keyvault set-policy
64+
Connection:
65+
- keep-alive
66+
ParameterSetName:
67+
- -g --name --spn --secret-permissions
68+
User-Agent:
69+
- AZURECLI/2.70.0 azsdk-python-core/1.31.0 Python/3.12.9 (Windows-11-10.0.26100-SP0)
70+
method: GET
71+
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.KeyVault/vaults/kv-ssl-test000002?api-version=2023-02-01
72+
response:
73+
body:
74+
string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.KeyVault/vaults/kv-ssl-test000002","name":"kv-ssl-test000002","type":"Microsoft.KeyVault/vaults","location":"westeurope","tags":{},"systemData":{"createdBy":"[email protected]","createdByType":"User","createdAt":"2025-03-05T21:00:15.656Z","lastModifiedBy":"[email protected]","lastModifiedByType":"User","lastModifiedAt":"2025-03-05T21:00:15.656Z"},"properties":{"sku":{"family":"A","name":"standard"},"tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","accessPolicies":[],"enabledForDeployment":false,"enableSoftDelete":true,"softDeleteRetentionInDays":7,"enableRbacAuthorization":true,"vaultUri":"https://kv-ssl-test000002.vault.azure.net/","provisioningState":"Succeeded","publicNetworkAccess":"Enabled"}}'
75+
headers:
76+
cache-control:
77+
- no-cache
78+
content-length:
79+
- '834'
80+
content-type:
81+
- application/json; charset=utf-8
82+
date:
83+
- Wed, 05 Mar 2025 21:00:52 GMT
84+
expires:
85+
- '-1'
86+
pragma:
87+
- no-cache
88+
strict-transport-security:
89+
- max-age=31536000; includeSubDomains
90+
x-aspnet-version:
91+
- 4.0.30319
92+
x-cache:
93+
- CONFIG_NOCACHE
94+
x-content-type-options:
95+
- nosniff
96+
x-ms-keyvault-service-version:
97+
- 1.5.1491.0
98+
x-ms-ratelimit-remaining-subscription-global-reads:
99+
- '16499'
100+
x-msedge-ref:
101+
- 'Ref A: A373C60BBBF64BA3B5A74C6EE6A7B06B Ref B: BL2AA2030101019 Ref C: 2025-03-05T21:00:52Z'
102+
status:
103+
code: 200
104+
message: OK
105+
version: 1

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,6 +1857,26 @@ def test_webapp_ssl_import(self, resource_group, key_vault):
18571857
webapp_name), cert_thumbprint)
18581858
])
18591859

1860+
@unittest.skip("Flaky Test")
1861+
@ResourceGroupPreparer(location=WINDOWS_ASP_LOCATION_WEBAPP)
1862+
@KeyVaultPreparer(location=WINDOWS_ASP_LOCATION_WEBAPP, name_prefix='kv-ssl-test', name_len=20)
1863+
def test_webapp_ssl_import_no_app(self, resource_group, key_vault):
1864+
# Cert Generated using
1865+
# https://learn.microsoft.com/azure/app-service-web/web-sites-configure-ssl-certificate#bkmk_ssopenssl
1866+
pfx_file = os.path.join(TEST_DIR, 'server.pfx')
1867+
cert_password = 'test'
1868+
cert_thumbprint = '9E9735C45C792B03B3FFCCA614852B32EE71AD6B'
1869+
cert_name = 'test-cert'
1870+
self.cmd('keyvault set-policy -g {} --name {} --spn {} --secret-permissions get'.format(
1871+
resource_group, key_vault, 'Microsoft.Azure.WebSites'))
1872+
self.cmd('keyvault certificate import --name {} --vault-name {} --file "{}" --password {}'.format(
1873+
cert_name, key_vault, pfx_file, cert_password))
1874+
1875+
self.cmd('webapp config ssl import --resource-group {} --key-vault {} --key-vault-certificate-name {} --certificate-name {}'.format(resource_group, key_vault, cert_name, "test123"), checks=[
1876+
JMESPathCheck('thumbprint', cert_thumbprint),
1877+
JMESPathCheck('test123')
1878+
])
1879+
18601880
@unittest.skip("Flaky Test")
18611881
@ResourceGroupPreparer(parameter_name='kv_resource_group', location=WINDOWS_ASP_LOCATION_WEBAPP)
18621882
@ResourceGroupPreparer(location=WINDOWS_ASP_LOCATION_WEBAPP)

0 commit comments

Comments
 (0)