diff --git a/src/azure-cli/azure/cli/command_modules/appservice/_help.py b/src/azure-cli/azure/cli/command_modules/appservice/_help.py index e0dad92e98c..d963d3fcb5e 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/_help.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/_help.py @@ -1776,6 +1776,8 @@ examples: - name: Create a Managed Certificate for cname.mycustomdomain.com. text: az webapp config ssl create --resource-group MyResourceGroup --name MyWebapp --hostname cname.mycustomdomain.com + - name: Create a Managed Certificate and wait for it to complete (up to 10 minutes). + text: az webapp config ssl create --resource-group MyResourceGroup --name MyWebapp --hostname cname.mycustomdomain.com --wait """ helps['webapp config storage-account'] = """ diff --git a/src/azure-cli/azure/cli/command_modules/appservice/_params.py b/src/azure-cli/azure/cli/command_modules/appservice/_params.py index bd4dc0b7ea5..9f0354f75b5 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/_params.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/_params.py @@ -521,6 +521,9 @@ def load_arguments(self, _): c.argument('hostname', help='The custom domain name') c.argument('name', options_list=['--name', '-n'], help='Name of the web app.') c.argument('resource-group', options_list=['--resource-group', '-g'], help='Name of resource group.') + c.argument('wait', options_list=['--wait'], action='store_true', default=False, + help='Wait up to 10 minutes for the certificate to be created. ' + 'Returns an error if creation times out instead of silently returning.') with self.argument_context(scope + ' config hostname') as c: c.argument('hostname', completer=get_hostname_completion_list, help="hostname assigned to the site, such as custom domains", id_part='child_name_1') diff --git a/src/azure-cli/azure/cli/command_modules/appservice/custom.py b/src/azure-cli/azure/cli/command_modules/appservice/custom.py index 386ba088608..c330a698c55 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/custom.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/custom.py @@ -5860,7 +5860,7 @@ def _get_cert(certificate_password, certificate_file): def list_ssl_certs(cmd, resource_group_name): client = web_client_factory(cmd.cli_ctx) - return client.certificates.list_by_resource_group(resource_group_name) + return list(client.certificates.list_by_resource_group(resource_group_name)) def show_ssl_cert(cmd, resource_group_name, certificate_name): @@ -5870,8 +5870,7 @@ def show_ssl_cert(cmd, resource_group_name, certificate_name): def delete_ssl_cert(cmd, resource_group_name, certificate_thumbprint): client = web_client_factory(cmd.cli_ctx) - webapp_certs = client.certificates.list_by_resource_group(resource_group_name) - for webapp_cert in webapp_certs: + for webapp_cert in client.certificates.list_by_resource_group(resource_group_name): if webapp_cert.thumbprint == certificate_thumbprint: return client.certificates.delete(resource_group_name, webapp_cert.name) raise ResourceNotFoundError("Certificate for thumbprint '{}' not found".format(certificate_thumbprint)) @@ -5960,7 +5959,7 @@ def import_ssl_cert(cmd, resource_group_name, key_vault, key_vault_certificate_n certificate_envelope=kv_cert_def) -def create_managed_ssl_cert(cmd, resource_group_name, name, hostname, slot=None, certificate_name=None): +def create_managed_ssl_cert(cmd, resource_group_name, name, hostname, slot=None, certificate_name=None, wait=False): Certificate = cmd.get_models('Certificate') hostname = hostname.lower() client = web_client_factory(cmd.cli_ctx) @@ -5997,7 +5996,8 @@ def create_managed_ssl_cert(cmd, resource_group_name, name, hostname, slot=None, poll_url = ex.response.headers['Location'] if 'Location' in ex.response.headers else None if ex.response.status_code == 202 and poll_url: r = send_raw_request(cmd.cli_ctx, method='get', url=poll_url) - poll_timeout = time.time() + 60 * 2 # 2 minute timeout + poll_timeout_minutes = 10 if wait else 2 + poll_timeout = time.time() + 60 * poll_timeout_minutes while r.status_code != 200 and time.time() < poll_timeout: time.sleep(5) @@ -6008,6 +6008,11 @@ def create_managed_ssl_cert(cmd, resource_group_name, name, hostname, slot=None, return r.json() except ValueError: return r.text + if wait: + raise CLIError("Managed Certificate creation for '{}' timed out after {} minutes. " + "Check status with 'az webapp config ssl show -g {} " + "--certificate-name {}'.".format(hostname, poll_timeout_minutes, + resource_group_name, certificate_name)) logger.warning("Managed Certificate creation in progress. Please use the command " "'az webapp config ssl show -g %s --certificate-name %s' " " to view your certificate once it is created", resource_group_name, certificate_name) @@ -6042,40 +6047,43 @@ def _check_service_principal_permissions(cmd, resource_group_name, key_vault_nam def _update_host_name_ssl_state(cmd, resource_group_name, webapp_name, webapp, host_name, ssl_state, thumbprint, slot=None): - Site, HostNameSslState = cmd.get_models('Site', 'HostNameSslState') - updated_webapp = Site(host_name_ssl_states=[HostNameSslState(name=host_name, - ssl_state=ssl_state, - thumbprint=thumbprint, - to_update=True)], - location=webapp.location, tags=webapp.tags) + HostNameSslState = cmd.get_models('HostNameSslState') + webapp.host_name_ssl_states = [HostNameSslState(name=host_name, + ssl_state=ssl_state, + thumbprint=thumbprint, + to_update=True)] return _generic_site_operation(cmd.cli_ctx, resource_group_name, webapp_name, 'begin_create_or_update', - slot, updated_webapp) + slot, webapp) def _update_ssl_binding(cmd, resource_group_name, name, certificate_thumbprint, ssl_type, hostname, slot=None): client = web_client_factory(cmd.cli_ctx) - webapp = client.web_apps.get(resource_group_name, name) + if slot: + webapp = client.web_apps.get_slot(resource_group_name, name, slot) + else: + webapp = client.web_apps.get(resource_group_name, name) if not webapp: raise ResourceNotFoundError("'{}' app doesn't exist".format(name)) cert_resource_group_name = parse_resource_id(webapp.server_farm_id)['resource_group'] - webapp_certs = client.certificates.list_by_resource_group(cert_resource_group_name) found_cert = None # search for a cert that matches in the app service plan's RG - for webapp_cert in webapp_certs: + for webapp_cert in client.certificates.list_by_resource_group(cert_resource_group_name): if webapp_cert.thumbprint == certificate_thumbprint: found_cert = webapp_cert + break # search for a cert that matches in the webapp's RG if not found_cert: - webapp_certs = client.certificates.list_by_resource_group(resource_group_name) - for webapp_cert in webapp_certs: + for webapp_cert in client.certificates.list_by_resource_group(resource_group_name): if webapp_cert.thumbprint == certificate_thumbprint: found_cert = webapp_cert + break # search for a cert that matches in the subscription, filtering on the serverfarm if not found_cert: - sub_certs = client.certificates.list(filter=f"ServerFarmId eq '{webapp.server_farm_id}'") - found_cert = next(iter([c for c in sub_certs if c.thumbprint == certificate_thumbprint]), None) + found_cert = next((c for c in client.certificates.list( + filter=f"ServerFarmId eq '{webapp.server_farm_id}'") + if c.thumbprint == certificate_thumbprint), None) if found_cert: if not hostname: if len(found_cert.host_names) == 1 and not found_cert.host_names[0].startswith('*'): diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/recordings/test_webapp_ssl.yaml b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/recordings/test_webapp_ssl.yaml index 115b33f6e69..8000de4135e 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/recordings/test_webapp_ssl.yaml +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/recordings/test_webapp_ssl.yaml @@ -3476,22 +3476,22 @@ interactions: User-Agent: - AZURECLI/2.79.0 azsdk-python-core/1.35.0 Python/3.13.9 (Windows-11-10.0.26200-SP0) method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003?api-version=2024-11-01 + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003/slots/slot-ssl-test000004?api-version=2024-11-01 response: body: - string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003","name":"web-ssl-test000003","type":"Microsoft.Web/sites","kind":"app","location":"West - Europe","tags":{"web":"web1"},"properties":{"name":"web-ssl-test000003","state":"Running","hostNames":["web-ssl-test000003.azurewebsites.net"],"webSpace":"clitest.rg000001-WestEuropewebspace","selfLink":"https://waws-prod-am2-019.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/clitest.rg000001-WestEuropewebspace/sites/web-ssl-test000003","repositorySiteName":"web-ssl-test000003","owner":null,"usageState":"Normal","enabled":true,"adminEnabled":true,"siteScopedCertificatesEnabled":false,"afdEnabled":false,"enabledHostNames":["web-ssl-test000003.azurewebsites.net","web-ssl-test000003.scm.azurewebsites.net"],"siteProperties":{"metadata":null,"properties":[{"name":"LinuxFxVersion","value":""},{"name":"WindowsFxVersion","value":null}],"appSettings":null},"availabilityState":"Normal","sslCertificates":null,"csrs":[],"cers":null,"siteMode":null,"hostNameSslStates":[{"name":"web-ssl-test000003.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Standard"},{"name":"web-ssl-test000003.scm.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Repository"}],"computeMode":null,"serverFarm":null,"serverFarmId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/serverfarms/ssl-test-plan000002","reserved":false,"isXenon":false,"hyperV":false,"sandboxType":null,"lastModifiedTimeUtc":"2025-10-30T22:13:18.483","storageRecoveryDefaultState":"Running","contentAvailabilityState":"Normal","runtimeAvailabilityState":"Normal","dnsConfiguration":{},"containerAllocationSubnet":null,"useContainerLocalhostBindings":null,"outboundVnetRouting":{"allTraffic":false,"applicationTraffic":false,"contentShareTraffic":false,"imagePullTraffic":false,"backupRestoreTraffic":false,"managedIdentityTraffic":false},"legacyServiceEndpointTrafficEvaluation":null,"siteConfig":{"numberOfWorkers":1,"defaultDocuments":null,"netFrameworkVersion":null,"phpVersion":null,"pythonVersion":null,"nodeVersion":null,"powerShellVersion":null,"linuxFxVersion":"","windowsFxVersion":null,"sandboxType":null,"windowsConfiguredStacks":null,"requestTracingEnabled":null,"remoteDebuggingEnabled":null,"remoteDebuggingVersion":null,"httpLoggingEnabled":null,"azureMonitorLogCategories":null,"acrUseManagedIdentityCreds":false,"acrUserManagedIdentityID":null,"logsDirectorySizeLimit":null,"detailedErrorLoggingEnabled":null,"publishingUsername":null,"publishingPassword":null,"appSettings":null,"metadata":null,"connectionStrings":null,"machineKey":null,"handlerMappings":null,"documentRoot":null,"scmType":null,"use32BitWorkerProcess":null,"webSocketsEnabled":null,"alwaysOn":true,"javaVersion":null,"javaContainer":null,"javaContainerVersion":null,"appCommandLine":null,"managedPipelineMode":null,"virtualApplications":null,"winAuthAdminState":null,"winAuthTenantState":null,"customAppPoolIdentityAdminState":null,"customAppPoolIdentityTenantState":null,"runtimeADUser":null,"runtimeADUserPassword":null,"loadBalancing":null,"routingRules":null,"experiments":null,"limits":null,"autoHealEnabled":null,"autoHealRules":null,"tracingOptions":null,"vnetName":null,"vnetRouteAllEnabled":null,"vnetPrivatePortsCount":null,"publicNetworkAccess":null,"cors":null,"push":null,"apiDefinition":null,"apiManagementConfig":null,"autoSwapSlotName":null,"localMySqlEnabled":null,"managedServiceIdentityId":null,"xManagedServiceIdentityId":null,"keyVaultReferenceIdentity":null,"ipSecurityRestrictions":null,"ipSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictions":null,"scmIpSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictionsUseMain":null,"http20Enabled":true,"minTlsVersion":null,"minTlsCipherSuite":null,"scmMinTlsCipherSuite":null,"supportedTlsCipherSuites":null,"scmSupportedTlsCipherSuites":null,"scmMinTlsVersion":null,"ftpsState":null,"preWarmedInstanceCount":null,"functionAppScaleLimit":0,"elasticWebAppScaleLimit":null,"healthCheckPath":null,"fileChangeAuditEnabled":null,"functionsRuntimeScaleMonitoringEnabled":null,"websiteTimeZone":null,"minimumElasticInstanceCount":0,"azureStorageAccounts":null,"http20ProxyFlag":null,"sitePort":null,"antivirusScanEnabled":null,"storageType":null,"sitePrivateLinkHostEnabled":null,"clusteringEnabled":false,"webJobsEnabled":false},"functionAppConfig":null,"daprConfig":null,"deploymentId":"web-ssl-test000003","slotName":null,"trafficManagerHostNames":null,"sku":"Standard","scmSiteAlsoStopped":false,"targetSwapSlot":null,"hostingEnvironment":null,"hostingEnvironmentProfile":null,"clientAffinityEnabled":true,"clientAffinityProxyEnabled":false,"useQueryStringAffinity":false,"blockPathTraversal":false,"clientCertEnabled":false,"clientCertMode":"Required","clientCertExclusionPaths":null,"clientCertExclusionEndPoints":null,"hostNamesDisabled":false,"ipMode":"IPv4","domainVerificationIdentifiers":null,"customDomainVerificationId":"06A754DDDA9E82CEAB4064B1FFBB341F2D951D8E36BC157906AEE1799EE3B407","kind":"app","managedEnvironmentId":null,"workloadProfileName":null,"resourceConfig":null,"inboundIpAddress":"13.69.68.36,104.45.14.249","possibleInboundIpAddresses":"13.69.68.36,104.45.14.249,13.69.68.36","inboundIpv6Address":"2603:1020:206:7::4a","possibleInboundIpv6Addresses":"2603:1020:206:7::4a","ftpUsername":"web-ssl-test000003\\$web-ssl-test000003","ftpsHostName":"ftps://waws-prod-am2-019.ftp.azurewebsites.windows.net/site/wwwroot","outboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,13.69.68.36,104.45.14.249","possibleOutboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,108.142.104.60,13.80.0.253,13.80.6.186,40.118.12.139,13.80.6.154,13.80.6.191,20.101.207.109,20.23.64.188,20.23.67.119,20.23.67.191,20.23.68.207,20.23.69.216,108.141.244.55,108.141.244.75,108.141.244.80,108.141.244.85,108.141.244.124,108.141.244.167,13.69.68.36,104.45.14.249","outboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","possibleOutboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:203:17::3ea,2603:1020:203:a::405,2603:1020:203:11::3b1,2603:1020:203:f::3e6,2603:1020:203:6::3fa,2603:1020:203:f::3e8,2603:1020:203:5::3ca,2603:1020:203:1f::3cc,2603:1020:203:a::409,2603:1020:203:12::3c5,2603:1020:203:d::3d4,2603:1020:203:b::3e9,2603:1020:203:5::3cc,2603:1020:203:1a::3b5,2603:1020:203:15::3d0,2603:1020:203:7::105,2603:1020:203:e::3de,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","containerSize":0,"dailyMemoryTimeQuota":0,"suspendedTill":null,"siteDisabledReason":0,"functionExecutionUnitsCache":null,"maxNumberOfWorkers":null,"homeStamp":"waws-prod-am2-019","cloningInfo":null,"hostingEnvironmentId":null,"tags":{"web":"web1"},"resourceGroup":"clitest.rg000001","defaultHostName":"web-ssl-test000003.azurewebsites.net","slotSwapStatus":null,"httpsOnly":true,"endToEndEncryptionEnabled":false,"functionsRuntimeAdminIsolationEnabled":false,"redundancyMode":"None","inProgressOperationId":null,"geoDistributions":null,"privateEndpointConnections":[],"publicNetworkAccess":"Enabled","buildVersion":null,"targetBuildVersion":null,"migrationState":null,"eligibleLogCategories":"AppServiceAppLogs,AppServiceAuditLogs,AppServiceConsoleLogs,AppServiceHTTPLogs,AppServiceIPSecAuditLogs,AppServicePlatformLogs,ScanLogs,AppServiceAuthenticationLogs","inFlightFeatures":["SiteContainers","RouteGeoCapacityClientTrafficToV2","RouteOperationClientTrafficToV2","RouteGeoPlanClientTrafficToV2","RouteGeoSourceControlKeyClientTrafficToV2","RouteGeoUserClientTrafficToV2"],"storageAccountRequired":false,"virtualNetworkSubnetId":null,"keyVaultReferenceIdentity":"SystemAssigned","autoGeneratedDomainNameLabelScope":null,"privateLinkIdentifiers":null,"sshEnabled":null}}' + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003/slots/slot-ssl-test000004","name":"web-ssl-test000003/slot-ssl-test000004","type":"Microsoft.Web/sites/slots","kind":"app","location":"West + Europe","properties":{"name":"web-ssl-test000003(slot-ssl-test000004)","state":"Running","hostNames":["web-ssl-test000003-slot-ssl-test000004.azurewebsites.net"],"webSpace":"clitest.rg000001-WestEuropewebspace","selfLink":"https://waws-prod-am2-019.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/clitest.rg000001-WestEuropewebspace/sites/web-ssl-test000003","repositorySiteName":"web-ssl-test000003","owner":null,"usageState":"Normal","enabled":true,"adminEnabled":true,"siteScopedCertificatesEnabled":false,"afdEnabled":false,"enabledHostNames":["web-ssl-test000003-slot-ssl-test000004.azurewebsites.net","web-ssl-test000003-slot-ssl-test000004.scm.azurewebsites.net"],"siteProperties":{"metadata":null,"properties":[{"name":"LinuxFxVersion","value":""},{"name":"WindowsFxVersion","value":null}],"appSettings":null},"availabilityState":"Normal","sslCertificates":null,"csrs":[],"cers":null,"siteMode":null,"hostNameSslStates":[{"name":"web-ssl-test000003-slot-ssl-test000004.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Standard"},{"name":"web-ssl-test000003-slot-ssl-test000004.scm.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Repository"}],"computeMode":null,"serverFarm":null,"serverFarmId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/serverfarms/ssl-test-plan000002","reserved":false,"isXenon":false,"hyperV":false,"sandboxType":null,"lastModifiedTimeUtc":"2025-10-30T22:13:55.393","storageRecoveryDefaultState":"Running","contentAvailabilityState":"Normal","runtimeAvailabilityState":"Normal","dnsConfiguration":{},"containerAllocationSubnet":null,"useContainerLocalhostBindings":null,"outboundVnetRouting":{"allTraffic":false,"applicationTraffic":false,"contentShareTraffic":false,"imagePullTraffic":false,"backupRestoreTraffic":false,"managedIdentityTraffic":false},"legacyServiceEndpointTrafficEvaluation":null,"siteConfig":{"numberOfWorkers":1,"defaultDocuments":null,"netFrameworkVersion":null,"phpVersion":null,"pythonVersion":null,"nodeVersion":null,"powerShellVersion":null,"linuxFxVersion":"","windowsFxVersion":null,"sandboxType":null,"windowsConfiguredStacks":null,"requestTracingEnabled":null,"remoteDebuggingEnabled":null,"remoteDebuggingVersion":null,"httpLoggingEnabled":null,"azureMonitorLogCategories":null,"acrUseManagedIdentityCreds":false,"acrUserManagedIdentityID":null,"logsDirectorySizeLimit":null,"detailedErrorLoggingEnabled":null,"publishingUsername":null,"publishingPassword":null,"appSettings":null,"metadata":null,"connectionStrings":null,"machineKey":null,"handlerMappings":null,"documentRoot":null,"scmType":null,"use32BitWorkerProcess":null,"webSocketsEnabled":null,"alwaysOn":false,"javaVersion":null,"javaContainer":null,"javaContainerVersion":null,"appCommandLine":null,"managedPipelineMode":null,"virtualApplications":null,"winAuthAdminState":null,"winAuthTenantState":null,"customAppPoolIdentityAdminState":null,"customAppPoolIdentityTenantState":null,"runtimeADUser":null,"runtimeADUserPassword":null,"loadBalancing":null,"routingRules":null,"experiments":null,"limits":null,"autoHealEnabled":null,"autoHealRules":null,"tracingOptions":null,"vnetName":null,"vnetRouteAllEnabled":null,"vnetPrivatePortsCount":null,"publicNetworkAccess":null,"cors":null,"push":null,"apiDefinition":null,"apiManagementConfig":null,"autoSwapSlotName":null,"localMySqlEnabled":null,"managedServiceIdentityId":null,"xManagedServiceIdentityId":null,"keyVaultReferenceIdentity":null,"ipSecurityRestrictions":null,"ipSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictions":null,"scmIpSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictionsUseMain":null,"http20Enabled":true,"minTlsVersion":null,"minTlsCipherSuite":null,"scmMinTlsCipherSuite":null,"supportedTlsCipherSuites":null,"scmSupportedTlsCipherSuites":null,"scmMinTlsVersion":null,"ftpsState":null,"preWarmedInstanceCount":null,"functionAppScaleLimit":0,"elasticWebAppScaleLimit":null,"healthCheckPath":null,"fileChangeAuditEnabled":null,"functionsRuntimeScaleMonitoringEnabled":null,"websiteTimeZone":null,"minimumElasticInstanceCount":0,"azureStorageAccounts":null,"http20ProxyFlag":null,"sitePort":null,"antivirusScanEnabled":null,"storageType":null,"sitePrivateLinkHostEnabled":null,"clusteringEnabled":false,"webJobsEnabled":false},"functionAppConfig":null,"daprConfig":null,"deploymentId":"web-ssl-test000003__b662","slotName":null,"trafficManagerHostNames":null,"sku":"Standard","scmSiteAlsoStopped":false,"targetSwapSlot":null,"hostingEnvironment":null,"hostingEnvironmentProfile":null,"clientAffinityEnabled":true,"clientAffinityProxyEnabled":false,"useQueryStringAffinity":false,"blockPathTraversal":false,"clientCertEnabled":false,"clientCertMode":"Required","clientCertExclusionPaths":null,"clientCertExclusionEndPoints":null,"hostNamesDisabled":false,"ipMode":"IPv4","domainVerificationIdentifiers":null,"customDomainVerificationId":"06A754DDDA9E82CEAB4064B1FFBB341F2D951D8E36BC157906AEE1799EE3B407","kind":"app","managedEnvironmentId":null,"workloadProfileName":null,"resourceConfig":null,"inboundIpAddress":"13.69.68.36,104.45.14.249","possibleInboundIpAddresses":"13.69.68.36,104.45.14.249,13.69.68.36","inboundIpv6Address":"2603:1020:206:7::4a","possibleInboundIpv6Addresses":"2603:1020:206:7::4a","ftpUsername":"web-ssl-test000003__slot-ssl-test000004\\$web-ssl-test000003__slot-ssl-test000004","ftpsHostName":"ftps://waws-prod-am2-019.ftp.azurewebsites.windows.net/site/wwwroot","outboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,13.69.68.36,104.45.14.249","possibleOutboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,108.142.104.60,13.80.0.253,13.80.6.186,40.118.12.139,13.80.6.154,13.80.6.191,20.101.207.109,20.23.64.188,20.23.67.119,20.23.67.191,20.23.68.207,20.23.69.216,108.141.244.55,108.141.244.75,108.141.244.80,108.141.244.85,108.141.244.124,108.141.244.167,13.69.68.36,104.45.14.249","outboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","possibleOutboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:203:17::3ea,2603:1020:203:a::405,2603:1020:203:11::3b1,2603:1020:203:f::3e6,2603:1020:203:6::3fa,2603:1020:203:f::3e8,2603:1020:203:5::3ca,2603:1020:203:1f::3cc,2603:1020:203:a::409,2603:1020:203:12::3c5,2603:1020:203:d::3d4,2603:1020:203:b::3e9,2603:1020:203:5::3cc,2603:1020:203:1a::3b5,2603:1020:203:15::3d0,2603:1020:203:7::105,2603:1020:203:e::3de,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","containerSize":0,"dailyMemoryTimeQuota":0,"suspendedTill":null,"siteDisabledReason":0,"functionExecutionUnitsCache":null,"maxNumberOfWorkers":null,"homeStamp":"waws-prod-am2-019","cloningInfo":null,"hostingEnvironmentId":null,"tags":null,"resourceGroup":"clitest.rg000001","defaultHostName":"web-ssl-test000003-slot-ssl-test000004.azurewebsites.net","slotSwapStatus":null,"httpsOnly":true,"endToEndEncryptionEnabled":false,"functionsRuntimeAdminIsolationEnabled":false,"redundancyMode":"None","inProgressOperationId":null,"geoDistributions":null,"privateEndpointConnections":[],"publicNetworkAccess":"Enabled","buildVersion":null,"targetBuildVersion":null,"migrationState":null,"eligibleLogCategories":"AppServiceAppLogs,AppServiceAuditLogs,AppServiceConsoleLogs,AppServiceHTTPLogs,AppServiceIPSecAuditLogs,AppServicePlatformLogs,ScanLogs,AppServiceAuthenticationLogs","inFlightFeatures":["SiteContainers","RouteGeoCapacityClientTrafficToV2","RouteOperationClientTrafficToV2","RouteGeoPlanClientTrafficToV2","RouteGeoSourceControlKeyClientTrafficToV2","RouteGeoUserClientTrafficToV2"],"storageAccountRequired":false,"virtualNetworkSubnetId":null,"keyVaultReferenceIdentity":"SystemAssigned","autoGeneratedDomainNameLabelScope":null,"privateLinkIdentifiers":null,"sshEnabled":null}}' headers: cache-control: - no-cache content-length: - - '8403' + - '8613' content-type: - application/json date: - - Thu, 30 Oct 2025 22:14:02 GMT + - Thu, 30 Oct 2025 22:14:00 GMT etag: - - '"1DC49EA654A8630"' + - '"1DC49EA7B4A8B10"' expires: - '-1' pragma: @@ -3507,7 +3507,7 @@ interactions: x-ms-ratelimit-remaining-subscription-global-reads: - '16499' x-msedge-ref: - - 'Ref A: 4131094C4BAE48908A206E6D56ED0A57 Ref B: SN4AA2022304017 Ref C: 2025-10-30T22:14:02Z' + - 'Ref A: 51E447BEEBEE49328ED874720B59E09F Ref B: SN4AA2022301029 Ref C: 2025-10-30T22:13:59Z' x-powered-by: - ASP.NET status: @@ -4182,22 +4182,22 @@ interactions: User-Agent: - AZURECLI/2.79.0 azsdk-python-core/1.35.0 Python/3.13.9 (Windows-11-10.0.26200-SP0) method: GET - uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003?api-version=2024-11-01 + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003/slots/slot-ssl-test000004?api-version=2024-11-01 response: body: - string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003","name":"web-ssl-test000003","type":"Microsoft.Web/sites","kind":"app","location":"West - Europe","tags":{"web":"web1"},"properties":{"name":"web-ssl-test000003","state":"Running","hostNames":["web-ssl-test000003.azurewebsites.net"],"webSpace":"clitest.rg000001-WestEuropewebspace","selfLink":"https://waws-prod-am2-019.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/clitest.rg000001-WestEuropewebspace/sites/web-ssl-test000003","repositorySiteName":"web-ssl-test000003","owner":null,"usageState":"Normal","enabled":true,"adminEnabled":true,"siteScopedCertificatesEnabled":false,"afdEnabled":false,"enabledHostNames":["web-ssl-test000003.azurewebsites.net","web-ssl-test000003.scm.azurewebsites.net"],"siteProperties":{"metadata":null,"properties":[{"name":"LinuxFxVersion","value":""},{"name":"WindowsFxVersion","value":null}],"appSettings":null},"availabilityState":"Normal","sslCertificates":null,"csrs":[],"cers":null,"siteMode":null,"hostNameSslStates":[{"name":"web-ssl-test000003.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Standard"},{"name":"web-ssl-test000003.scm.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Repository"}],"computeMode":null,"serverFarm":null,"serverFarmId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/serverfarms/ssl-test-plan000002","reserved":false,"isXenon":false,"hyperV":false,"sandboxType":null,"lastModifiedTimeUtc":"2025-10-30T22:13:18.483","storageRecoveryDefaultState":"Running","contentAvailabilityState":"Normal","runtimeAvailabilityState":"Normal","dnsConfiguration":{},"containerAllocationSubnet":null,"useContainerLocalhostBindings":null,"outboundVnetRouting":{"allTraffic":false,"applicationTraffic":false,"contentShareTraffic":false,"imagePullTraffic":false,"backupRestoreTraffic":false,"managedIdentityTraffic":false},"legacyServiceEndpointTrafficEvaluation":null,"siteConfig":{"numberOfWorkers":1,"defaultDocuments":null,"netFrameworkVersion":null,"phpVersion":null,"pythonVersion":null,"nodeVersion":null,"powerShellVersion":null,"linuxFxVersion":"","windowsFxVersion":null,"sandboxType":null,"windowsConfiguredStacks":null,"requestTracingEnabled":null,"remoteDebuggingEnabled":null,"remoteDebuggingVersion":null,"httpLoggingEnabled":null,"azureMonitorLogCategories":null,"acrUseManagedIdentityCreds":false,"acrUserManagedIdentityID":null,"logsDirectorySizeLimit":null,"detailedErrorLoggingEnabled":null,"publishingUsername":null,"publishingPassword":null,"appSettings":null,"metadata":null,"connectionStrings":null,"machineKey":null,"handlerMappings":null,"documentRoot":null,"scmType":null,"use32BitWorkerProcess":null,"webSocketsEnabled":null,"alwaysOn":true,"javaVersion":null,"javaContainer":null,"javaContainerVersion":null,"appCommandLine":null,"managedPipelineMode":null,"virtualApplications":null,"winAuthAdminState":null,"winAuthTenantState":null,"customAppPoolIdentityAdminState":null,"customAppPoolIdentityTenantState":null,"runtimeADUser":null,"runtimeADUserPassword":null,"loadBalancing":null,"routingRules":null,"experiments":null,"limits":null,"autoHealEnabled":null,"autoHealRules":null,"tracingOptions":null,"vnetName":null,"vnetRouteAllEnabled":null,"vnetPrivatePortsCount":null,"publicNetworkAccess":null,"cors":null,"push":null,"apiDefinition":null,"apiManagementConfig":null,"autoSwapSlotName":null,"localMySqlEnabled":null,"managedServiceIdentityId":null,"xManagedServiceIdentityId":null,"keyVaultReferenceIdentity":null,"ipSecurityRestrictions":null,"ipSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictions":null,"scmIpSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictionsUseMain":null,"http20Enabled":true,"minTlsVersion":null,"minTlsCipherSuite":null,"scmMinTlsCipherSuite":null,"supportedTlsCipherSuites":null,"scmSupportedTlsCipherSuites":null,"scmMinTlsVersion":null,"ftpsState":null,"preWarmedInstanceCount":null,"functionAppScaleLimit":0,"elasticWebAppScaleLimit":null,"healthCheckPath":null,"fileChangeAuditEnabled":null,"functionsRuntimeScaleMonitoringEnabled":null,"websiteTimeZone":null,"minimumElasticInstanceCount":0,"azureStorageAccounts":null,"http20ProxyFlag":null,"sitePort":null,"antivirusScanEnabled":null,"storageType":null,"sitePrivateLinkHostEnabled":null,"clusteringEnabled":false,"webJobsEnabled":false},"functionAppConfig":null,"daprConfig":null,"deploymentId":"web-ssl-test000003","slotName":null,"trafficManagerHostNames":null,"sku":"Standard","scmSiteAlsoStopped":false,"targetSwapSlot":null,"hostingEnvironment":null,"hostingEnvironmentProfile":null,"clientAffinityEnabled":true,"clientAffinityProxyEnabled":false,"useQueryStringAffinity":false,"blockPathTraversal":false,"clientCertEnabled":false,"clientCertMode":"Required","clientCertExclusionPaths":null,"clientCertExclusionEndPoints":null,"hostNamesDisabled":false,"ipMode":"IPv4","domainVerificationIdentifiers":null,"customDomainVerificationId":"06A754DDDA9E82CEAB4064B1FFBB341F2D951D8E36BC157906AEE1799EE3B407","kind":"app","managedEnvironmentId":null,"workloadProfileName":null,"resourceConfig":null,"inboundIpAddress":"13.69.68.36,104.45.14.249","possibleInboundIpAddresses":"13.69.68.36,104.45.14.249,13.69.68.36","inboundIpv6Address":"2603:1020:206:7::4a","possibleInboundIpv6Addresses":"2603:1020:206:7::4a","ftpUsername":"web-ssl-test000003\\$web-ssl-test000003","ftpsHostName":"ftps://waws-prod-am2-019.ftp.azurewebsites.windows.net/site/wwwroot","outboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,13.69.68.36,104.45.14.249","possibleOutboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,108.142.104.60,13.80.0.253,13.80.6.186,40.118.12.139,13.80.6.154,13.80.6.191,20.101.207.109,20.23.64.188,20.23.67.119,20.23.67.191,20.23.68.207,20.23.69.216,108.141.244.55,108.141.244.75,108.141.244.80,108.141.244.85,108.141.244.124,108.141.244.167,13.69.68.36,104.45.14.249","outboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","possibleOutboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:203:17::3ea,2603:1020:203:a::405,2603:1020:203:11::3b1,2603:1020:203:f::3e6,2603:1020:203:6::3fa,2603:1020:203:f::3e8,2603:1020:203:5::3ca,2603:1020:203:1f::3cc,2603:1020:203:a::409,2603:1020:203:12::3c5,2603:1020:203:d::3d4,2603:1020:203:b::3e9,2603:1020:203:5::3cc,2603:1020:203:1a::3b5,2603:1020:203:15::3d0,2603:1020:203:7::105,2603:1020:203:e::3de,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","containerSize":0,"dailyMemoryTimeQuota":0,"suspendedTill":null,"siteDisabledReason":0,"functionExecutionUnitsCache":null,"maxNumberOfWorkers":null,"homeStamp":"waws-prod-am2-019","cloningInfo":null,"hostingEnvironmentId":null,"tags":{"web":"web1"},"resourceGroup":"clitest.rg000001","defaultHostName":"web-ssl-test000003.azurewebsites.net","slotSwapStatus":null,"httpsOnly":true,"endToEndEncryptionEnabled":false,"functionsRuntimeAdminIsolationEnabled":false,"redundancyMode":"None","inProgressOperationId":null,"geoDistributions":null,"privateEndpointConnections":[],"publicNetworkAccess":"Enabled","buildVersion":null,"targetBuildVersion":null,"migrationState":null,"eligibleLogCategories":"AppServiceAppLogs,AppServiceAuditLogs,AppServiceConsoleLogs,AppServiceHTTPLogs,AppServiceIPSecAuditLogs,AppServicePlatformLogs,ScanLogs,AppServiceAuthenticationLogs","inFlightFeatures":["SiteContainers","RouteGeoCapacityClientTrafficToV2","RouteOperationClientTrafficToV2","RouteGeoPlanClientTrafficToV2","RouteGeoSourceControlKeyClientTrafficToV2","RouteGeoUserClientTrafficToV2"],"storageAccountRequired":false,"virtualNetworkSubnetId":null,"keyVaultReferenceIdentity":"SystemAssigned","autoGeneratedDomainNameLabelScope":null,"privateLinkIdentifiers":null,"sshEnabled":null}}' + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/sites/web-ssl-test000003/slots/slot-ssl-test000004","name":"web-ssl-test000003/slot-ssl-test000004","type":"Microsoft.Web/sites/slots","kind":"app","location":"West + Europe","properties":{"name":"web-ssl-test000003(slot-ssl-test000004)","state":"Running","hostNames":["web-ssl-test000003-slot-ssl-test000004.azurewebsites.net"],"webSpace":"clitest.rg000001-WestEuropewebspace","selfLink":"https://waws-prod-am2-019.api.azurewebsites.windows.net:454/subscriptions/00000000-0000-0000-0000-000000000000/webspaces/clitest.rg000001-WestEuropewebspace/sites/web-ssl-test000003","repositorySiteName":"web-ssl-test000003","owner":null,"usageState":"Normal","enabled":true,"adminEnabled":true,"siteScopedCertificatesEnabled":false,"afdEnabled":false,"enabledHostNames":["web-ssl-test000003-slot-ssl-test000004.azurewebsites.net","web-ssl-test000003-slot-ssl-test000004.scm.azurewebsites.net"],"siteProperties":{"metadata":null,"properties":[{"name":"LinuxFxVersion","value":""},{"name":"WindowsFxVersion","value":null}],"appSettings":null},"availabilityState":"Normal","sslCertificates":null,"csrs":[],"cers":null,"siteMode":null,"hostNameSslStates":[{"name":"web-ssl-test000003-slot-ssl-test000004.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Standard"},{"name":"web-ssl-test000003-slot-ssl-test000004.scm.azurewebsites.net","sslState":"Disabled","ipBasedSslResult":null,"virtualIP":null,"virtualIPv6":null,"thumbprint":null,"certificateResourceId":null,"toUpdate":null,"toUpdateIpBasedSsl":null,"ipBasedSslState":"NotConfigured","hostType":"Repository"}],"computeMode":null,"serverFarm":null,"serverFarmId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/clitest.rg000001/providers/Microsoft.Web/serverfarms/ssl-test-plan000002","reserved":false,"isXenon":false,"hyperV":false,"sandboxType":null,"lastModifiedTimeUtc":"2025-10-30T22:13:55.393","storageRecoveryDefaultState":"Running","contentAvailabilityState":"Normal","runtimeAvailabilityState":"Normal","dnsConfiguration":{},"containerAllocationSubnet":null,"useContainerLocalhostBindings":null,"outboundVnetRouting":{"allTraffic":false,"applicationTraffic":false,"contentShareTraffic":false,"imagePullTraffic":false,"backupRestoreTraffic":false,"managedIdentityTraffic":false},"legacyServiceEndpointTrafficEvaluation":null,"siteConfig":{"numberOfWorkers":1,"defaultDocuments":null,"netFrameworkVersion":null,"phpVersion":null,"pythonVersion":null,"nodeVersion":null,"powerShellVersion":null,"linuxFxVersion":"","windowsFxVersion":null,"sandboxType":null,"windowsConfiguredStacks":null,"requestTracingEnabled":null,"remoteDebuggingEnabled":null,"remoteDebuggingVersion":null,"httpLoggingEnabled":null,"azureMonitorLogCategories":null,"acrUseManagedIdentityCreds":false,"acrUserManagedIdentityID":null,"logsDirectorySizeLimit":null,"detailedErrorLoggingEnabled":null,"publishingUsername":null,"publishingPassword":null,"appSettings":null,"metadata":null,"connectionStrings":null,"machineKey":null,"handlerMappings":null,"documentRoot":null,"scmType":null,"use32BitWorkerProcess":null,"webSocketsEnabled":null,"alwaysOn":false,"javaVersion":null,"javaContainer":null,"javaContainerVersion":null,"appCommandLine":null,"managedPipelineMode":null,"virtualApplications":null,"winAuthAdminState":null,"winAuthTenantState":null,"customAppPoolIdentityAdminState":null,"customAppPoolIdentityTenantState":null,"runtimeADUser":null,"runtimeADUserPassword":null,"loadBalancing":null,"routingRules":null,"experiments":null,"limits":null,"autoHealEnabled":null,"autoHealRules":null,"tracingOptions":null,"vnetName":null,"vnetRouteAllEnabled":null,"vnetPrivatePortsCount":null,"publicNetworkAccess":null,"cors":null,"push":null,"apiDefinition":null,"apiManagementConfig":null,"autoSwapSlotName":null,"localMySqlEnabled":null,"managedServiceIdentityId":null,"xManagedServiceIdentityId":null,"keyVaultReferenceIdentity":null,"ipSecurityRestrictions":null,"ipSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictions":null,"scmIpSecurityRestrictionsDefaultAction":null,"scmIpSecurityRestrictionsUseMain":null,"http20Enabled":true,"minTlsVersion":null,"minTlsCipherSuite":null,"scmMinTlsCipherSuite":null,"supportedTlsCipherSuites":null,"scmSupportedTlsCipherSuites":null,"scmMinTlsVersion":null,"ftpsState":null,"preWarmedInstanceCount":null,"functionAppScaleLimit":0,"elasticWebAppScaleLimit":null,"healthCheckPath":null,"fileChangeAuditEnabled":null,"functionsRuntimeScaleMonitoringEnabled":null,"websiteTimeZone":null,"minimumElasticInstanceCount":0,"azureStorageAccounts":null,"http20ProxyFlag":null,"sitePort":null,"antivirusScanEnabled":null,"storageType":null,"sitePrivateLinkHostEnabled":null,"clusteringEnabled":false,"webJobsEnabled":false},"functionAppConfig":null,"daprConfig":null,"deploymentId":"web-ssl-test000003__b662","slotName":null,"trafficManagerHostNames":null,"sku":"Standard","scmSiteAlsoStopped":false,"targetSwapSlot":null,"hostingEnvironment":null,"hostingEnvironmentProfile":null,"clientAffinityEnabled":true,"clientAffinityProxyEnabled":false,"useQueryStringAffinity":false,"blockPathTraversal":false,"clientCertEnabled":false,"clientCertMode":"Required","clientCertExclusionPaths":null,"clientCertExclusionEndPoints":null,"hostNamesDisabled":false,"ipMode":"IPv4","domainVerificationIdentifiers":null,"customDomainVerificationId":"06A754DDDA9E82CEAB4064B1FFBB341F2D951D8E36BC157906AEE1799EE3B407","kind":"app","managedEnvironmentId":null,"workloadProfileName":null,"resourceConfig":null,"inboundIpAddress":"13.69.68.36,104.45.14.249","possibleInboundIpAddresses":"13.69.68.36,104.45.14.249,13.69.68.36","inboundIpv6Address":"2603:1020:206:7::4a","possibleInboundIpv6Addresses":"2603:1020:206:7::4a","ftpUsername":"web-ssl-test000003__slot-ssl-test000004\\$web-ssl-test000003__slot-ssl-test000004","ftpsHostName":"ftps://waws-prod-am2-019.ftp.azurewebsites.windows.net/site/wwwroot","outboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,13.69.68.36,104.45.14.249","possibleOutboundIpAddresses":"104.45.14.250,104.45.14.251,104.45.14.252,104.45.14.253,108.142.104.60,13.80.0.253,13.80.6.186,40.118.12.139,13.80.6.154,13.80.6.191,20.101.207.109,20.23.64.188,20.23.67.119,20.23.67.191,20.23.68.207,20.23.69.216,108.141.244.55,108.141.244.75,108.141.244.80,108.141.244.85,108.141.244.124,108.141.244.167,13.69.68.36,104.45.14.249","outboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","possibleOutboundIpv6Addresses":"2603:1020:203:f::3e3,2603:1020:203:10::39d,2603:1020:203:10::3a0,2603:1020:203:1e::361,2603:1020:203:17::3ea,2603:1020:203:a::405,2603:1020:203:11::3b1,2603:1020:203:f::3e6,2603:1020:203:6::3fa,2603:1020:203:f::3e8,2603:1020:203:5::3ca,2603:1020:203:1f::3cc,2603:1020:203:a::409,2603:1020:203:12::3c5,2603:1020:203:d::3d4,2603:1020:203:b::3e9,2603:1020:203:5::3cc,2603:1020:203:1a::3b5,2603:1020:203:15::3d0,2603:1020:203:7::105,2603:1020:203:e::3de,2603:1020:206:7::4a,2603:10e1:100:2::682d:ef9","containerSize":0,"dailyMemoryTimeQuota":0,"suspendedTill":null,"siteDisabledReason":0,"functionExecutionUnitsCache":null,"maxNumberOfWorkers":null,"homeStamp":"waws-prod-am2-019","cloningInfo":null,"hostingEnvironmentId":null,"tags":null,"resourceGroup":"clitest.rg000001","defaultHostName":"web-ssl-test000003-slot-ssl-test000004.azurewebsites.net","slotSwapStatus":null,"httpsOnly":true,"endToEndEncryptionEnabled":false,"functionsRuntimeAdminIsolationEnabled":false,"redundancyMode":"None","inProgressOperationId":null,"geoDistributions":null,"privateEndpointConnections":[],"publicNetworkAccess":"Enabled","buildVersion":null,"targetBuildVersion":null,"migrationState":null,"eligibleLogCategories":"AppServiceAppLogs,AppServiceAuditLogs,AppServiceConsoleLogs,AppServiceHTTPLogs,AppServiceIPSecAuditLogs,AppServicePlatformLogs,ScanLogs,AppServiceAuthenticationLogs","inFlightFeatures":["SiteContainers","RouteGeoCapacityClientTrafficToV2","RouteOperationClientTrafficToV2","RouteGeoPlanClientTrafficToV2","RouteGeoSourceControlKeyClientTrafficToV2","RouteGeoUserClientTrafficToV2"],"storageAccountRequired":false,"virtualNetworkSubnetId":null,"keyVaultReferenceIdentity":"SystemAssigned","autoGeneratedDomainNameLabelScope":null,"privateLinkIdentifiers":null,"sshEnabled":null}}' headers: cache-control: - no-cache content-length: - - '8403' + - '8613' content-type: - application/json date: - - Thu, 30 Oct 2025 22:14:15 GMT + - Thu, 30 Oct 2025 22:14:00 GMT etag: - - '"1DC49EA654A8630"' + - '"1DC49EA7B4A8B10"' expires: - '-1' pragma: @@ -4213,7 +4213,7 @@ interactions: x-ms-ratelimit-remaining-subscription-global-reads: - '16499' x-msedge-ref: - - 'Ref A: 00DC694BD9AB4B8A9F057BF9133D7A3C Ref B: SN4AA2022305035 Ref C: 2025-10-30T22:14:16Z' + - 'Ref A: 51E447BEEBEE49328ED874720B59E09F Ref B: SN4AA2022301029 Ref C: 2025-10-30T22:13:59Z' x-powered-by: - ASP.NET status: diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 853eadc1edd..dca5df75cd3 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -12,7 +12,8 @@ from knack.util import CLIError from azure.cli.core.azclierror import (InvalidArgumentValueError, MutuallyExclusiveArgumentError, - AzureResponseError) + AzureResponseError, + ResourceNotFoundError) from azure.cli.command_modules.appservice.custom import (set_deployment_user, update_git_token, add_hostname, update_site_configs, @@ -20,6 +21,7 @@ view_in_browser, sync_site_repo, _match_host_names_from_cert, + _update_host_name_ssl_state, bind_ssl_cert, list_publish_profiles, show_app, @@ -30,6 +32,8 @@ list_snapshots, restore_snapshot, create_managed_ssl_cert, + list_ssl_certs, + delete_ssl_cert, add_github_actions, update_app_settings, update_application_settings_polling, @@ -469,6 +473,94 @@ def test_create_managed_ssl_cert(self, generic_site_op_mock, client_factory_mock certificate_envelope=cert_def) + @mock.patch('azure.cli.command_modules.appservice.custom.send_raw_request', autospec=True) + @mock.patch('azure.cli.command_modules.appservice.custom._verify_hostname_binding', autospec=True) + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + @mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation', autospec=True) + def test_create_managed_ssl_cert_wait_timeout_raises_error(self, generic_site_op_mock, client_factory_mock, + verify_binding_mock, send_raw_request_mock): + """Test that --wait raises CLIError on timeout instead of returning None.""" + webapp_name = 'someWebAppName' + rg_name = 'someRgName' + farm_id = '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Web/serverfarms/farm1' + host_name = 'www.contoso.com' + + client = mock.MagicMock() + client_factory_mock.return_value = client + cmd_mock = _get_test_cmd() + cli_ctx_mock = mock.MagicMock() + cli_ctx_mock.data = {'subscription_id': 'sub1'} + cmd_mock.cli_ctx = cli_ctx_mock + Site, Certificate = cmd_mock.get_models('Site', 'Certificate') + site = Site(name=webapp_name, location='westeurope') + site.server_farm_id = farm_id + generic_site_op_mock.return_value = site + verify_binding_mock.return_value = True + + # Simulate 202 with Location header + ex_response = mock.MagicMock() + ex_response.status_code = 202 + ex_response.headers = {'Location': 'https://polling-url'} + api_exception = Exception('accepted') + api_exception.response = ex_response + client.certificates.create_or_update.side_effect = api_exception + + # Polling always returns 202 (never completes) + poll_response = mock.MagicMock() + poll_response.status_code = 202 + send_raw_request_mock.return_value = poll_response + + # With wait=True and mocked time to simulate immediate timeout + with mock.patch('azure.cli.command_modules.appservice.custom.time') as time_mock: + time_mock.time.side_effect = [0, 999999] # Start, then past timeout + time_mock.sleep = mock.MagicMock() + with self.assertRaises(CLIError): + create_managed_ssl_cert(cmd_mock, rg_name, webapp_name, host_name, None, wait=True) + + @mock.patch('azure.cli.command_modules.appservice.custom.send_raw_request', autospec=True) + @mock.patch('azure.cli.command_modules.appservice.custom._verify_hostname_binding', autospec=True) + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + @mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation', autospec=True) + def test_create_managed_ssl_cert_no_wait_returns_none(self, generic_site_op_mock, client_factory_mock, + verify_binding_mock, send_raw_request_mock): + """Test that without --wait, timeout returns None with a warning (default behavior).""" + webapp_name = 'someWebAppName' + rg_name = 'someRgName' + farm_id = '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.Web/serverfarms/farm1' + host_name = 'www.contoso.com' + + client = mock.MagicMock() + client_factory_mock.return_value = client + cmd_mock = _get_test_cmd() + cli_ctx_mock = mock.MagicMock() + cli_ctx_mock.data = {'subscription_id': 'sub1'} + cmd_mock.cli_ctx = cli_ctx_mock + Site, Certificate = cmd_mock.get_models('Site', 'Certificate') + site = Site(name=webapp_name, location='westeurope') + site.server_farm_id = farm_id + generic_site_op_mock.return_value = site + verify_binding_mock.return_value = True + + # Simulate 202 with Location header + ex_response = mock.MagicMock() + ex_response.status_code = 202 + ex_response.headers = {'Location': 'https://polling-url'} + api_exception = Exception('accepted') + api_exception.response = ex_response + client.certificates.create_or_update.side_effect = api_exception + + # Polling always returns 202 + poll_response = mock.MagicMock() + poll_response.status_code = 202 + send_raw_request_mock.return_value = poll_response + + # Without wait (default), should return None, not raise + with mock.patch('azure.cli.command_modules.appservice.custom.time') as time_mock: + time_mock.time.side_effect = [0, 999999] + time_mock.sleep = mock.MagicMock() + result = create_managed_ssl_cert(cmd_mock, rg_name, webapp_name, host_name, None, wait=False) + self.assertIsNone(result) + def test_update_app_settings_error_handling_no_parameters(self): """Test that MutuallyExclusiveArgumentError is raised when neither settings nor slot_settings are provided.""" cmd_mock = _get_test_cmd() @@ -639,10 +731,175 @@ def test_update_webapp_platform_release_channel_latest(self): self.assertEqual(result.additional_properties["properties"]["platformReleaseChannel"], "Latest") +class TestUpdateHostNameSslState(unittest.TestCase): + @mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation', autospec=True) + def test_update_host_name_ssl_state_passes_full_site(self, generic_site_op_mock): + """Test that _update_host_name_ssl_state passes the full Site object (not a partial one) + to begin_create_or_update, preserving policy-sensitive fields like https_only.""" + cmd_mock = _get_test_cmd() + Site, HostNameSslState, SslState = cmd_mock.get_models('Site', 'HostNameSslState', 'SslState') + + webapp = Site(name='mySite', location='eastus', tags={'env': 'prod'}) + webapp.https_only = True + webapp.host_name_ssl_states = [ + HostNameSslState(name='existing.contoso.com', + ssl_state=SslState.sni_enabled, + thumbprint='EXISTINGTHUMB') + ] + + _update_host_name_ssl_state(cmd_mock, 'myRg', 'mySite', webapp, + 'www.contoso.com', SslState.sni_enabled, 'NEWTHUMB') + + generic_site_op_mock.assert_called_once() + call_args = generic_site_op_mock.call_args + passed_site = call_args[0][5] # (cli_ctx, rg, name, op, slot, site) + + # The passed object should be the original webapp, preserving all fields + self.assertTrue(passed_site.https_only, + "https_only must be preserved to avoid Azure Policy denial") + self.assertEqual(passed_site.location, 'eastus') + self.assertEqual(passed_site.tags, {'env': 'prod'}) + + # host_name_ssl_states should contain only the binding being updated + self.assertEqual(len(passed_site.host_name_ssl_states), 1) + ssl_state = passed_site.host_name_ssl_states[0] + self.assertEqual(ssl_state.name, 'www.contoso.com') + self.assertEqual(ssl_state.thumbprint, 'NEWTHUMB') + self.assertTrue(ssl_state.to_update) + + @mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation', autospec=True) + def test_update_host_name_ssl_state_with_slot(self, generic_site_op_mock): + """Test that slot parameter is correctly forwarded.""" + cmd_mock = _get_test_cmd() + Site, SslState = cmd_mock.get_models('Site', 'SslState') + webapp = Site(name='mySite', location='westus') + + _update_host_name_ssl_state(cmd_mock, 'myRg', 'mySite', webapp, + 'www.contoso.com', SslState.sni_enabled, 'THUMB', slot='staging') + + call_args = generic_site_op_mock.call_args + # slot is the 5th positional arg (index 4) after cli_ctx, rg, name, operation_name + self.assertEqual(call_args[0][4], 'staging') + # site is the 6th positional arg (index 5); verify it has the expected properties + site_arg = call_args[0][5] + self.assertEqual(site_arg.name, webapp.name) + self.assertEqual(site_arg.location, webapp.location) + + class FakedResponse: # pylint: disable=too-few-public-methods def __init__(self, status_code): self.status_code = status_code +class _FakePagedIterator: + """Simulates an Azure SDK paged iterator that yields items across multiple pages. + + Unlike a plain list or iter(), this class mimics the SDK behavior where + items are fetched page-by-page via continuation tokens. The pages_fetched + property tracks how many pages have been consumed, allowing tests to verify + both full-consumption (list_ssl_certs) and early-break (delete, bind) behavior. + """ + + def __init__(self, pages): + """pages: list of lists, each inner list represents one page of results.""" + self._pages = pages + self._page_fetch_count = 0 + + def __iter__(self): + for page in self._pages: + self._page_fetch_count += 1 + yield from page + + @property + def pages_fetched(self): + return self._page_fetch_count + + +def _make_cert(name, thumbprint=''): + cert = mock.MagicMock() + cert.name = name + cert.thumbprint = thumbprint + return cert + + +class TestSSLCertPagination(unittest.TestCase): + """Tests for SSL certificate pagination fix (#29403, #28722, #27950).""" + + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + def test_list_ssl_certs_returns_all_pages(self, client_factory_mock): + """Ensure list_ssl_certs fully consumes the pager and returns a concrete list.""" + cmd_mock = _get_test_cmd() + + page1 = [_make_cert('cert1'), _make_cert('cert2')] + page2 = [_make_cert('cert3')] + pager = _FakePagedIterator([page1, page2]) + + client = mock.MagicMock() + client_factory_mock.return_value = client + client.certificates.list_by_resource_group.return_value = pager + + result = list_ssl_certs(cmd_mock, 'myRG') + + # Must be a concrete list (not a lazy iterator) so the CLI framework + # can serialize all results, and must contain items from every page. + self.assertIsInstance(result, list) + self.assertEqual(len(result), 3) + self.assertEqual(pager.pages_fetched, 2) + client.certificates.list_by_resource_group.assert_called_once_with('myRG') + + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + def test_delete_ssl_cert_finds_cert_beyond_first_page(self, client_factory_mock): + """Ensure delete_ssl_cert can find a cert on a later page.""" + cmd_mock = _get_test_cmd() + + # Target cert is on page 2 — would be missed without full pagination + page1 = [_make_cert(f'cert{i}', f'THUMB{i:04d}') for i in range(50)] + page2 = [_make_cert(f'cert{i}', f'THUMB{i:04d}') for i in range(50, 100)] + pager = _FakePagedIterator([page1, page2]) + + client = mock.MagicMock() + client_factory_mock.return_value = client + client.certificates.list_by_resource_group.return_value = pager + + delete_ssl_cert(cmd_mock, 'myRG', 'THUMB0099') + + # The cert on page 2 must be found and deleted + self.assertEqual(pager.pages_fetched, 2) + client.certificates.delete.assert_called_once_with('myRG', 'cert99') + + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + def test_delete_ssl_cert_early_break_skips_remaining_pages(self, client_factory_mock): + """Ensure delete_ssl_cert stops iterating once the cert is found (lazy).""" + cmd_mock = _get_test_cmd() + + page1 = [_make_cert('cert0', 'TARGET')] + page2 = [_make_cert('cert1', 'OTHER')] + pager = _FakePagedIterator([page1, page2]) + + client = mock.MagicMock() + client_factory_mock.return_value = client + client.certificates.list_by_resource_group.return_value = pager + + delete_ssl_cert(cmd_mock, 'myRG', 'TARGET') + + # Only page 1 should be fetched — early break avoids page 2 + self.assertEqual(pager.pages_fetched, 1) + client.certificates.delete.assert_called_once_with('myRG', 'cert0') + + @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory', autospec=True) + def test_delete_ssl_cert_not_found_raises_error(self, client_factory_mock): + """Ensure delete_ssl_cert raises ResourceNotFoundError for missing thumbprint.""" + cmd_mock = _get_test_cmd() + + pager = _FakePagedIterator([[_make_cert('cert1', 'AAAA')]]) + + client = mock.MagicMock() + client_factory_mock.return_value = client + client.certificates.list_by_resource_group.return_value = pager + + with self.assertRaises(ResourceNotFoundError): + delete_ssl_cert(cmd_mock, 'myRG', 'NONEXISTENT') + + if __name__ == '__main__': unittest.main()