diff --git a/aspnetcore/fundamentals/servers/httpsys.md b/aspnetcore/fundamentals/servers/httpsys.md
index 8bcd1adbd6cf..fbcd65b1764b 100644
--- a/aspnetcore/fundamentals/servers/httpsys.md
+++ b/aspnetcore/fundamentals/servers/httpsys.md
@@ -1,10 +1,11 @@
---
title: HTTP.sys web server implementation in ASP.NET Core
-author: rick-anderson
-description: Introduces HTTP.sys, a web server for ASP.NET Core on Windows. Built on the Http.Sys kernel mode driver, HTTP.sys is an alternative to Kestrel that can be used for direct connection to the Internet without IIS.
+author: tdykstra
+description: Learn about HTTP.sys, a web server for ASP.NET Core on Windows. Built on the HTTP.sys kernel-mode driver, HTTP.sys is an alternative to Kestrel that can be used for direct connection to the Internet without IIS.
manager: wpickett
-ms.author: riande
-ms.date: 08/07/2017
+ms.author: tdykstra
+ms.custom: mvc
+ms.date: 02/28/2018
ms.prod: asp.net-core
ms.technology: aspnet
ms.topic: article
@@ -12,168 +13,157 @@ uid: fundamentals/servers/httpsys
---
# HTTP.sys web server implementation in ASP.NET Core
-By [Tom Dykstra](https://github.com/tdykstra) and [Chris Ross](https://github.com/Tratcher)
+By [Tom Dykstra](https://github.com/tdykstra), [Chris Ross](https://github.com/Tratcher), and [Luke Latham](https://github.com/guardrex)
> [!NOTE]
-> This topic applies only to ASP.NET Core 2.0 and later. In earlier versions of ASP.NET Core, HTTP.sys is named [WebListener](xref:fundamentals/servers/weblistener).
+> This topic applies to ASP.NET Core 2.0 or later. In earlier versions of ASP.NET Core, HTTP.sys is named [WebListener](xref:fundamentals/servers/weblistener).
-HTTP.sys is a [web server for ASP.NET Core](index.md) that runs only on Windows. It's built on the [Http.Sys kernel mode driver](https://msdn.microsoft.com/library/windows/desktop/aa364510.aspx). HTTP.sys is an alternative to [Kestrel](kestrel.md) that offers some features that Kestel doesn't. **HTTP.sys can't be used with IIS or IIS Express, as it's incompatible with the [ASP.NET Core Module](aspnet-core-module.md).**
+[HTTP.sys](/iis/get-started/introduction-to-iis/introduction-to-iis-architecture#hypertext-transfer-protocol-stack-httpsys) is a [web server for ASP.NET Core](xref:fundamentals/servers/index) that only runs on Windows. HTTP.sys is an alternative to [Kestrel](xref:fundamentals/servers/kestrel) and offers some features that Kestrel doesn't provide.
+
+> [!IMPORTANT]
+> HTTP.sys is incompatible with the [ASP.NET Core Module](xref:fundamentals/servers/aspnet-core-module) and can't be used with IIS or IIS Express.
HTTP.sys supports the following features:
-- [Windows Authentication](xref:security/authentication/windowsauth)
-- Port sharing
-- HTTPS with SNI
-- HTTP/2 over TLS (Windows 10)
-- Direct file transmission
-- Response caching
-- WebSockets (Windows 8)
+* [Windows Authentication](xref:security/authentication/windowsauth)
+* Port sharing
+* HTTPS with SNI
+* HTTP/2 over TLS (Windows 10 or later)
+* Direct file transmission
+* Response caching
+* WebSockets (Windows 8 or later)
Supported Windows versions:
-- Windows 7 and Windows Server 2008 R2 and later
+* Windows 7 or later
+* Windows Server 2008 R2 or later
[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/fundamentals/servers/httpsys/sample) ([how to download](xref:tutorials/index#how-to-download-a-sample))
## When to use HTTP.sys
-HTTP.sys is useful for deployments where you need to expose the server directly to the Internet without using IIS.
-
-![HTTP.sys communicates directly with the Internet](httpsys/_static/httpsys-to-internet.png)
-
-Because it's built on Http.Sys, HTTP.sys doesn't require a reverse proxy server for protection against attacks. Http.Sys is mature technology that protects against many kinds of attacks and provides the robustness, security, and scalability of a full-featured web server. IIS itself runs as an HTTP listener on top of Http.Sys.
-
-HTTP.sys is a good choice for internal deployments when you need a feature not available in Kestrel, such as Windows authentication.
-
-![HTTP.sys communicates directly with your internal network](httpsys/_static/httpsys-to-internal.png)
-
-## How to use HTTP.sys
-
-Here's an overview of setup tasks for the host OS and your ASP.NET Core application.
-
-### Configure Windows Server
-
-* Install the version of .NET that your application requires, such as [.NET Core](https://www.microsoft.com/net/download/core) or [.NET Framework](https://www.microsoft.com/net/download/framework).
-
-* Preregister URL prefixes to bind to HTTP.sys, and set up SSL certificates
-
- If you don't preregister URL prefixes in Windows, you have to run your application with administrator privileges. The only exception is if you bind to localhost using HTTP (not HTTPS) with a port number greater than 1024; in that case, administrator privileges aren't required.
-
- For details, see [How to preregister prefixes and configure SSL](#preregister-url-prefixes-and-configure-ssl) later in this article.
-
-* Open firewall ports to allow traffic to reach HTTP.sys.
+HTTP.sys is useful for deployments where:
- You can use *netsh.exe* or [PowerShell cmdlets](https://technet.microsoft.com/library/jj554906).
+* There's a need to expose the server directly to the Internet without using IIS.
-There are also [Http.Sys registry settings](https://support.microsoft.com/kb/820129).
+ ![HTTP.sys communicates directly with the Internet](httpsys/_static/httpsys-to-internet.png)
-### Configure your ASP.NET Core application to use HTTP.sys
+* An internal deployment requires a feature not available in Kestrel, such as [Windows Authentication](xref:security/authentication/windowsauth).
-* No package install is needed if you use the [Microsoft.AspNetCore.All](xref:fundamentals/metapackage) metapackage. The [Microsoft.AspNetCore.Server.HttpSys](https://www.nuget.org/packages/Microsoft.AspNetCore.Server.HttpSys/) package is included in the metapackage.
+ ![HTTP.sys communicates directly with the internal network](httpsys/_static/httpsys-to-internal.png)
-* Call the `UseHttpSys` extension method on `WebHostBuilder` in your `Main` method, specifying any [HTTP.sys options](https://github.com/aspnet/HttpSysServer/blob/rel/2.0.0/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs) that you need, as shown in the following example:
+HTTP.sys is mature technology that protects against many types of attacks and provides the robustness, security, and scalability of a full-featured web server. IIS itself runs as an HTTP listener on top of HTTP.sys.
- [!code-csharp[](httpsys/sample/Program.cs?name=snippet_Main&highlight=11-19)]
-
-### Configure HTTP.sys options
-
-Here are some of the HTTP.sys settings and limits that you can configure.
+## How to use HTTP.sys
-**Maximum client connections**
+### Configure the ASP.NET Core app to use HTTP.sys
-The maximum number of concurrent open TCP connections can be set for the entire application with the following code in *Program.cs*:
+1. A package reference in the project file isn't required when using the [Microsoft.AspNetCore.All metapackage](xref:fundamentals/metapackage) ([nuget.org](https://www.nuget.org/packages/Microsoft.AspNetCore.All/)) (ASP.NET Core 2.0 or later). When not using the `Microsoft.AspNetCore.All` metapackage, add a package reference to [Microsoft.AspNetCore.Server.HttpSys](https://www.nuget.org/packages/Microsoft.AspNetCore.Server.HttpSys/).
-[!code-csharp[](httpsys/sample/Program.cs?name=snippet_Options&highlight=5)]
+1. Call the [UseHttpSys](/dotnet/api/microsoft.aspnetcore.hosting.webhostbuilderhttpsysextensions.usehttpsys) extension method when building the web host, specifying any required [HTTP.sys options](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions):
-The maximum number of connections is unlimited (null) by default.
+ [!code-csharp[](httpsys/sample/Program.cs?name=snippet1&highlight=4-12)]
-**Maximum request body size**
+ Additional HTTP.sys configuration is handled through [registry settings](https://support.microsoft.com/kb/820129).
-The default maximum request body size is 30,000,000 bytes, which is approximately 28.6MB.
+ **HTTP.sys options**
-The recommended way to override the limit in an ASP.NET Core MVC app is to use the [RequestSizeLimit](https://github.com/aspnet/Mvc/blob/rel/2.0.0/src/Microsoft.AspNetCore.Mvc.Core/RequestSizeLimitAttribute.cs) attribute on an action method:
+ | Property | Description | Default |
+ | -------- | ----------- | :-----: |
+ | [AllowSynchronousIO](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.allowsynchronousio) | Control whether synchronous input/output is allowed for the `HttpContext.Request.Body` and `HttpContext.Response.Body`. | `true` |
+ | [Authentication.AllowAnonymous](/dotnet/api/microsoft.aspnetcore.server.httpsys.authenticationmanager.allowanonymous) | Allow anonymous requests. | `true` |
+ | [Authentication.Schemes](/dotnet/api/microsoft.aspnetcore.server.httpsys.authenticationmanager.schemes) | Specify the allowed authentication schemes. May be modified at any time prior to disposing the listener. Values are provided by the [AuthenticationSchemes enum](/dotnet/api/microsoft.aspnetcore.server.httpsys.authenticationschemes): `Basic`, `Kerberos`, `Negotiate`, `None`, and `NTLM`. | `None` |
+ | [EnableResponseCaching](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.enableresponsecaching) | Attempt [kernel-mode](/windows-hardware/drivers/gettingstarted/user-mode-and-kernel-mode) caching for responses with eligible headers. The response may not include `Set-Cookie`, `Vary`, or `Pragma` headers. It must include a `Cache-Control` header that's `public` and either a `shared-max-age` or `max-age` value, or an `Expires` header. | `true` |
+ | [MaxAccepts](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.maxaccepts) | The maximum number of concurrent accepts. | 5 × [Environment.
ProcessorCount](/dotnet/api/system.environment.processorcount) |
+ | [MaxConnections](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.maxconnections) | The maximum number of concurrent connections to accept. Use `-1` for infinite. Use `null` to use the registry's machine-wide setting. | `null`
(unlimited) |
+ | [MaxRequestBodySize](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.maxrequestbodysize) | See the MaxRequestBodySize section. | 30000000 bytes
(~28.6 MB) |
+ | [RequestQueueLimit](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.requestqueuelimit) | The maximum number of requests that can be queued. | 1000 |
+ | [ThrowWriteExceptions](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.throwwriteexceptions) | Indicate if response body writes that fail due to client disconnects should throw exceptions or complete normally. | `false`
(complete normally) |
+ | [Timeouts](/dotnet/api/microsoft.aspnetcore.server.httpsys.httpsysoptions.timeouts) | Expose the HTTP.sys [TimeoutManager](/dotnet/api/microsoft.aspnetcore.server.httpsys.timeoutmanager) configuration, which may also be configured in the registry. Follow the API links to learn more about each setting, including default values:
+ Request ID: @Model.RequestId
+
+ Swapping to the Development environment displays detailed information about the error that occurred. +
+
+ The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app or adding .UseEnvironment("Development")
to WebHost
in Program.cs and restarting the app.
+
Run the app from a command prompt by executing the following command from the project's folder:
+dotnet run
Logging indicates that the HttpSysListener
is ready to receive requests:
+
info: Microsoft.AspNetCore.Server.HttpSys.HttpSysListener[0]
+ Start
+info: Microsoft.AspNetCore.Server.HttpSys.HttpSysListener[0]
+ Listening on prefix: http://localhost:5000/
+
+ Hosted by HTTP.sys
"); + var serverAddressesFeature = app.ServerFeatures.GetListening on the following addresses: {string.Join(", ", serverAddressesFeature.Addresses)}
"); - } + logger.LogInformation($"Addresses: {addresses}"); - await context.Response.WriteAsync($"Request URL: {context.Request.GetDisplayUrl()}
"); + await next.Invoke(); }); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseDatabaseErrorPage(); + } + else + { + app.UseExceptionHandler("/Error"); + } + + app.UseStaticFiles(); + app.UseMvc(); } -#endregion + #endregion } } diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.Development.json b/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.Development.json new file mode 100644 index 000000000000..fa8ce71a97a3 --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.Production.json b/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.Production.json new file mode 100644 index 000000000000..05d41950658c --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.Production.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Error", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.json b/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.json new file mode 100644 index 000000000000..20aa907654ab --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug" + } + } +} diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/bower.json b/aspnetcore/fundamentals/servers/httpsys/sample/bower.json new file mode 100644 index 000000000000..b07e3cc5ae5d --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/bower.json @@ -0,0 +1,10 @@ +{ + "name": "asp.net", + "private": true, + "dependencies": { + "bootstrap": "3.3.7", + "jquery": "2.2.0", + "jquery-validation": "1.14.0", + "jquery-validation-unobtrusive": "3.2.6" + } +} diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/bundleconfig.json b/aspnetcore/fundamentals/servers/httpsys/sample/bundleconfig.json new file mode 100644 index 000000000000..6d3f9a57aea2 --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/bundleconfig.json @@ -0,0 +1,24 @@ +// Configure bundling and minification for the project. +// More info at https://go.microsoft.com/fwlink/?LinkId=808241 +[ + { + "outputFileName": "wwwroot/css/site.min.css", + // An array of relative input file paths. Globbing patterns supported + "inputFiles": [ + "wwwroot/css/site.css" + ] + }, + { + "outputFileName": "wwwroot/js/site.min.js", + "inputFiles": [ + "wwwroot/js/site.js" + ], + // Optionally specify minification options + "minify": { + "enabled": true, + "renameLocals": true + }, + // Optionally generate .map file + "sourceMap": false + } +] diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/css/site.css b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/css/site.css new file mode 100644 index 000000000000..64634c996c41 --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/css/site.css @@ -0,0 +1,25 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} + +h1 { + font-size: 24px; +} + +h2 { + font-size: 20px; +} + +h3 { + font-size:16px +} + +.body-content { + padding-left: 15px; + padding-right: 15px; +} + +.panel-body { + font-size: 16px; +} diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/css/site.min.css b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/css/site.min.css new file mode 100644 index 000000000000..099af93ef1d3 --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/css/site.min.css @@ -0,0 +1 @@ +body{padding-top:50px;padding-bottom:20px}h1{font-size:24px}h2{font-size:20px}h3{font-size:16px}.body-content{padding-left:15px;padding-right:15px}.panel-body{font-size:16px} diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/favicon.ico b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/favicon.ico new file mode 100644 index 000000000000..a3a799985c43 Binary files /dev/null and b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/favicon.ico differ diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/js/site.js b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/js/site.js new file mode 100644 index 000000000000..82ecce7b4a78 --- /dev/null +++ b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/js/site.js @@ -0,0 +1 @@ +// Write your Javascript code. diff --git a/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/js/site.min.js b/aspnetcore/fundamentals/servers/httpsys/sample/wwwroot/js/site.min.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/aspnetcore/fundamentals/servers/kestrel.md b/aspnetcore/fundamentals/servers/kestrel.md index 5168465a14f6..3f19cbd563c7 100644 --- a/aspnetcore/fundamentals/servers/kestrel.md +++ b/aspnetcore/fundamentals/servers/kestrel.md @@ -179,7 +179,7 @@ The `Listen` method binds to a TCP socket, and an options lambda lets you config Notice how this example configures SSL for a particular endpoint by using [ListenOptions](https://github.com/aspnet/KestrelHttpServer/blob/rel/2.0.0/src/Microsoft.AspNetCore.Server.Kestrel.Core/ListenOptions.cs). You can use the same API to configure other Kestrel settings for particular endpoints. -[!INCLUDE[How to make an SSL cert](../../includes/make-ssl-cert.md)] +[!INCLUDE[How to make an X.509 cert](../../includes/make-x509-cert.md)] **Bind to a Unix socket** @@ -333,7 +333,7 @@ var host = new WebHostBuilder() > [!NOTE] > HTTPS and HTTP cannot be hosted on the same port. -[!INCLUDE[How to make an SSL cert](../../includes/make-ssl-cert.md)] +[!INCLUDE[How to make an X.509 cert](../../includes/make-x509-cert.md)] --- ## Next steps diff --git a/aspnetcore/includes/make-ssl-cert.md b/aspnetcore/includes/make-ssl-cert.md deleted file mode 100644 index 8f0d7b1b4dc3..000000000000 --- a/aspnetcore/includes/make-ssl-cert.md +++ /dev/null @@ -1,3 +0,0 @@ -For generating self-signed SSL certificates on Windows, you can use the PowerShell cmdlet [New-SelfSignedCertificate](/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps). For a third-party tool that makes it easier for you to generate self-signed certificates, see [SelfCert](https://www.pluralsight.com/blog/software-development/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net). - -On macOS and Linux you can create a self-signed certificate using [OpenSSL](https://www.openssl.org/). diff --git a/aspnetcore/includes/make-x509-cert.md b/aspnetcore/includes/make-x509-cert.md new file mode 100644 index 000000000000..f2e168a2caa9 --- /dev/null +++ b/aspnetcore/includes/make-x509-cert.md @@ -0,0 +1,3 @@ +On Windows, self-signed certificates can be created using the [New-SelfSignedCertificate PowerShell cmdlet](/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps). For an unsupported example, see [UpdateIISExpressSSLForChrome.ps1](https://github.com/aspnet/Docs/tree/master/aspnetcore/includes/make-x509-cert/UpdateIISExpressSSLForChrome.ps1). + +On macOS, Linux, and Windows, certificates can be created using [OpenSSL](https://www.openssl.org/). diff --git a/aspnetcore/includes/make-x509-cert/UpdateIISExpressSSLForChrome.ps1 b/aspnetcore/includes/make-x509-cert/UpdateIISExpressSSLForChrome.ps1 new file mode 100644 index 000000000000..756f4553bed3 --- /dev/null +++ b/aspnetcore/includes/make-x509-cert/UpdateIISExpressSSLForChrome.ps1 @@ -0,0 +1,63 @@ +# Create a new self-signed certificate for IIS Express. +# +# Provides a subjectAltName (SAN) to satisfy Chrome 58 or later. +# See https://bugs.chromium.org/p/chromium/issues/detail?id=308330 +# +# Run the script at an administrative PowerShell prompt. +# +# When prompted to trust a new certificate via a Windows dialog, +# select Yes. Otherwise, Visual Studio won't be able to determine +# the process ID when the web app is launched. +# +# THIS SCRIPT IS UNSUPPORTED BY MICROSOFT AND PROVIDED "AS IS" +# WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. + +$certificate = New-SelfSignedCertificate ` + -Subject localhost ` + -DnsName localhost ` + -KeyAlgorithm RSA ` + -KeyLength 2048 ` + -NotBefore (Get-Date) ` + -NotAfter (Get-Date).AddYears(5) ` + -CertStoreLocation "cert:CurrentUser\My" ` + -FriendlyName "IIS Express Development Certificate" ` + -HashAlgorithm SHA256 ` + -KeyUsage DigitalSignature, KeyEncipherment, DataEncipherment ` + -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") +$certificatePath = 'Cert:\CurrentUser\My\' + ($certificate.ThumbPrint) + +# Export the certificate to a PFX (PKCS #12). +$pfxPassword = ConvertTo-SecureString ([Guid]::NewGuid().ToString()) -Force -AsPlainText +$pfxFilePath = [system.io.path]::GetTempFileName() +$cerFilePath = [system.io.path]::GetTempFileName() + +Export-PfxCertificate -Cert $certificatePath -FilePath $pfxFilePath -Password $pfxPassword +Export-Certificate -Cert $certificatePath -FilePath $cerFilePath + +# Now that the certificate has been exported, delete the cert. +Remove-Item $certificatePath + +# Add the certificate to the machine personal store, so netsh can bind. +Import-PfxCertificate -FilePath $pfxFilePath Cert:\LocalMachine\My -Password $pfxPassword -Exportable + +# Add the certificate to the user root store, so trust is enabled. +# When the prompt appears to trust a new certificate via a Windows dialog, +# select Yes. Otherwise, Visual Studio won't be able to determine the +# process ID when the web app is launched. +Import-Certificate -FilePath $cerFilePath -CertStoreLocation Cert:\CurrentUser\Root + +# Bind using netsh. The app ID is the IIS Express app ID. +for ($port = 44300; $port -lt 44400; $port++) +{ + $command = "http delete sslcert ipport=0.0.0.0:$port" + Write-Output $command + $command | netsh + + $command = "http add sslcert ipport=0.0.0.0:$port certhash="+$($certificate.Thumbprint)+" appid={214124cd-d05b-4309-9af9-9caa44b2b74a}" + Write-Output $command + $command | netsh +} + +# Clean up the temporary PFX. +Remove-Item $pfxFilePath +Remove-Item $cerFilePath