The request was aborted: Could not create SSL/TLS secure channel

Since most Servers are moving towards TLS 1.3 and removing TLS 1.0/1.1 support, it is important to make note of certain Server configurations that might be required to make your .Net Framework Application compatible with new TLS versions like TLS 1.2.

Just upgrading the Application to latest .Net Framework like 4.8 version, which as per documentation states it automatically handles the compatibility with newer TLS versions when older TLS versions are disabled.

I have managed to resolve the issues on my server by updating the SSL Cipher Suite Order, I had mistakenly removed some of the suites that windows suggested was for TLS1.0 and 1.1 only when in actual fact they were needed for some TLS1.2 connections as well.

I resolved my issues by:

  1. Open Run Prompt and run gpedit.msc
  2. Navigate to “Administrative Templates > Network > SSL Configuration Settings”
  3. Open SSL Cipher Suite Order
  4. Select Enabled
  5. Paste the list of suites below into the text box (make sure there are no spaces)
  6. Click Apply
  7. Restart the server

SSL SUITES:

TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256_P256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384_P384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256_P256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384_P384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA_P256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA_P256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_256_CBC_SHA

Note, these suites work for me but you may require other ones for different applications. You should be able to find a full list and more info on the suites here https://docs.microsoft.com/en-us/windows/win32/secauthn/cipher-suites-in-schannel?redirectedfrom=MSDN

You can also use a tool like IISCrypto to update the Cipher Suite order.

Docker desktop requires the server service to be enabled Windows 10

While trying to install Docker Desktop through the installer on my Windows 10 machine I kept got the error as mentioned in the title of this post. The installer can be downloaded from here which is the Community version since we’re working on the development environment.

To resolve this, we need to take care of some prerequisites as mentioned below:

  1. Install Hyper-V and enable it. If not already done, run the following commands in PowerShell in Admin mode:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All

2. Once the above 2 commands are successfully completed. Open Services.msc in Admin mode and look for Server service. Enable this Service as per requirement to Automatic/Manual and set it to Running.

Restart your machine to complete the installation.

LogParser query example

I’ve found LogParser tool to be very useful for querying log files especially whenever I am required to analyze the IIS log files. You can download LogParser from here.

In this example, I’ll be querying multiple Log files unique users with Windows Authentication visiting the site. Click on the icon “Choose Log files/folders to query” and Add all files which you want to search. Open a New Query window and in the Query editor, enter the below query:

SELECT DISTINCT cs-username FROM '[LOGFILEPATH]'

This works much like SQL queries where IIS log headers work like columns. The above query will simply return distinct users visiting the site. Make sure the Log Type selected is W3CLOG.

Update to above example while searching for a QueryString and also getting the username count:

SELECT DISTINCT cs-username, COUNT(cs-username) FROM '[LOGFILEPATH]' WHERE cs-uri-query LIKE '%Excel%' GROUP BY cs-username

If you want to Output all the data to a .csv file, then you can use the below query:

SELECT DISTINCT cs-username INTO '[OUTFILEPATH]users.CSV' FROM '[LOGFILEPATH]'

You can check the default export directory where the file is created. It should be something like this “C:\Users\<username>\AppData\Roaming\ExLPT\Log Parser Studio\Output”.

Configure ODBC Data Source Administrator with SQL Server

You might often need to connect to the SQL Server database say in your classic ASP Application using the ODBC System DSN (Data Source Name). To do this, we need to use the 32-bit application odbcad32.exe under the SysWOW64 folder. The steps are as below:
Go to the SystemDSN tab -> Click on Add

Select the SQL Server driver SQLSRV32.DLL:

Give a name and Server details:

Provide the SQL Login details. You can also use Windows Authentication if it serves the purpose.

Select the required database by checking “Change the default database to:”

Click on Finish and Test Connection.

To use this in Classic ASP, add the DSN details in your cnconst file as below:

DSNName = "testdb"
SystemDSN = "DSN=" & DSNName & "; UID=xxxx; PWD=xxxx"

To connect, use the code below to fetch a recordset:

SET rsData = Server.CreateObject("ADODB.RecordSet")
strSQL = "SELECT LOCATION from Table-name"
rsData.Open strSQL , SystemDSN
Dim LocationsArr
LocationsArr = rsData.GetRows()

How to call a Stored Procedure from Classic ASP

ASP is a program based on Microsoft Technologies that runs inside a web server.

Below is the method used for calling a Stored Procedure in MSSQLSERVER using classic ASP scripting.

The variable SystemDSN holds the value of the Data Source Name that is configured using Odbcad.exe under the SysWow64 folder on the Server. Select the required driver for SQL Server for the DSN configuration to connect to your database.

Set cn = Server.CreateObject("ADODB.Connection")
cn.Open SystemDSN
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cn
cmd.CommandText = "usp_GetJSON"
cmd.CommandType = adCmdStoredProc

cmd.Parameters.Append(cmd.CreateParameter("@Ticket_Code",adInteger,adParamInput,100, Request.QueryString("inp_ticket_id")))
cmd.Parameters.Append(cmd.CreateParameter("@Username",adVarchar,adParamInput,100000,user))
cmd.Parameters.Append(cmd.CreateParameter("@OperatorID",adVarchar,adParamInput,100000,oprID))

cmd.Parameters.Append(cmd.CreateParameter("@ReturnJson", adVarchar, adParamOutput,100000))
cmd.Execute

'show alert in case of failure.
ReturnValue = cmd.Parameters("@ReturnJson").Value
if ReturnValue <> " " then
  jsonObject = ReturnValue
  'set the flag as 1 to ensure that JSON has been returned from SP to be used later.
  flag= "1"

end if

The above example shows the SP takes 3 input parameters and 1 output parameter. The SP returns a jsonObject assigned to the ReturnValue.

The ADO Connection Object is used to create an open connection to a data source which lets you access and manipulate a database. The ADO Command object is used to execute a single query against a database using which you can perform CRUD operations.

The json can be generated in the SP using the “FOR JSON PATH” feature for SQL Server. However, json is used just for this example. The return value can be anything as per your requirement.

How to read connection strings stored in appsettings file C#

This post is based on a setup of an asp.net core application. Configuration is read in the Startup class upon the Application startup. The Configure method in this class calls the ApiBootstrapper to check whether the connection string for Dev or Production is required.
This can be further used to call the Stored Procedures or query tables using ADO.Net.

Appsettings.json file is the asp.net core config file and is automatically read out. This file contains the Connection Strings is as shown below:

{
	"configSetting": {
		"ConnectionStrings": {
			"ProdConnection": "Data Source=ServerName;Initial Catalog=DBProd;UID=username;PWD=password;",
			"DevConnection": "Data Source=ServerName;Initial Catalog=DBDev;UID=username;PWD=password;"
		},
		"Parameters": {
			"IsProduction": true
			"IsDev": false
		}
	}
}
public class Startup
{
        public IConfiguration Configuration { get; }

	public Startup(IConfiguration configuration)
	{
		Configuration = configuration;
	}	
	
	public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
	{
		////
	
	
		ApiBootstrapper.Initialize(Configuration);
	}
	
}

Below is the code for ApiBootstrapper class:

public class ApiBootstrapper
{
	
	public static void Initialize(IConfiguration configuration)
	{
		CommonUtil.IsProduction = configuration.GetSection(ConfigKeys.configSetting.ToString()).GetSection(ConfigKeys.Log.ToString()).Value.ToString();
		if (Convert.ToBoolean(CommonUtil.IsProduction))
		{
			CommonUtil.ConnectionString = configuration.GetSection("configSetting").GetSection("ConnectionStrings").GetSection("ProdConnection").Value.ToString();
		}
	}
}

The above example shows how we can store Connection Strings for different environments like Dev and Prod and read it based on Config file settings.

If you’re simply using ConnectionStrings section in your appSettings.json file as below:

"ConnectionStrings": {
			"DefaultConnection": "...",
			"AnotherConnection": "..."
		}

The StartUp class will still have the IConfiguration Dependency Injection:

public class Startup
{
	public IConfiguration Configuration { get; }
	
	public Startup(IConfiguration configuration)
	{
		Configuration = configuration;
	}

	//...
}

If you’ve configured e.g. EFCore in your Application and need to refer the DefaultConnection while registering the Service for DBContext, you can do as follows:

public void ConfigureServices(IServiceCollection services)
{
	services.AddDbContext<AppDbContext>(options => 
		options.UseSqlServer(Configuration.GetConnectionStrings("DefaultConnection")));
		
	//...
}

When Configuration.GetConnectionStrings is called here, it will automatically search read the ConnectionStrings form appSettings and search for DefaultConnection.

How to Count number of e-mails in mailbox using PowerShell

First, you need to access the mailbox on the Microsoft Exchange Server and then get the mailbox statistics for the count of e-mails. The credentials you use should have admin access on the Server to be able to access the mailbox.

The following script uses some credentials stored in a file on the machine where the script is running and connects to a Session on the Microsoft Exchange Server:

$un = “admin@somedomain.com”
$Pass = cat “C:\Temp\Securestring.txt” | ConvertTo-SecureString
$Credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Un, $Pass

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://{servername}/PowerShell/ -Authentication Kerberos -Credential $Credentials

Import-PSSession $Session -DisableNameChecking

$data=Get-MailboxFolderStatistics -Identity "somemail@somedomain.com" -FolderScope inbox | ?{$_.folderpath -eq "/inbox"} | select ItemsInFolder 

Write-Host $data.ItemsInFolder

You can further use this data item to mail it to the Admins for Operations purpose.

To know how to send e-mail in PowerShell, check out this post here.

Configure Reverse Proxy in IIS with URL Rewrite

Reverse Proxy is an intermediate Server that might be exposed to the Internet that can help secure your incoming traffic from the Client and forwarding the request to a back-end service that might be on a Private network. This returns the response back to the Client and hides your Web Server from the Outside world.

You need the following IIS extensions for configuring IIS Reverse Proxy:

URL Rewrite:
https://www.iis.net/downloads/microsoft/url-rewrite

Application Request Routing:
https://www.iis.net/downloads/microsoft/application-request-routing

Now add the following URL Rewrite rule:

You’ll be prompted to enable the ARR to further enable Proxy functionality. Click on OK.

In the above window, you can also provide the Outbound configuration to map the response URLs From Private URL To Public URL conversions mapping.

For this example, accessing http://localhost:8087 will simply redirect to http://localhost:8084 and serve the Client.

Unable to find type System.Windows.MessageBox error PowerShell

If you’re using a Message Box in your PowerShell script, chances are you’re running into this error while running the script in a PowerShell window.
But this error does not occur in PowerShell ISE.

If you generally run the command in PowerShell as below and you get the error:

[System.Windows.MessageBox]::Show("Test box")

Then try adding the Type using the below command and run the above command again:

Add-Type -AssemblyName PresentationFramework
[System.Windows.MessageBox]::Show("Test box")

The above type is part of the PresentationFramework.dll that was added with WPF.

The other type is the System.Windows.Forms which was added with Windows Forms, and exists within the Windows Forms assemblies.

So, add the type as below:

Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show("Test box")

You can add these types in your script as per your need. If you’re script uses Params, just add the Add-Type line below it.

Debug classic asp application hosted on IIS with Visual Studio

Some non .Net Applications like the ones written in classic ASP are required to be debugged in Visual Studio. Since these are not hosted on IIS Express, but on IIS, you need to identify the worker process running your machine or the Server and attach the w3wp.exe with the Debug tool in Visual Studio.

Enable Debugging under IIS classic ASP section as shown below:

Under the Debug menu in Visual Studio, select “Attach to Process”:

There may be multiple worker processes running on the machine depending on how many applications are running under IIS. Match the right one with the correct ProcessID.

Add the debug points in your Asp file and hit the required Page in the browser.