Internal Server Error on SharePoint Server 2019 O-Premises after Update – Missing IIS Module “SPRequestFilterModule”.

Currently I investigate a problem. A SharePoint Server 2019 page showed me an Internal Server Error 500.

SharePoint Server 2019 is installed on Windows Server 2022. The August 2023 CU is applied.

After enabling “Failed Request Tracing” I saw this log message in “Failed Request Logs” on disk (C:\inetpub\logs\FailedReqLogFiles\W3SVCxxxxxxxx)

To enable “Failed Request Tracking”…

After I configured the “Failed Request Logging” I found a XML file on disk that is rendered as follows in the browser…

Now looking at the “Modules” config of the SharePoint web site in IIS I saw:

The module is missing…

I registered it by selecting the server level in the tree

Then… Open “Modules”

In the list the “SPRequestFilterModule” is missing:

On the right pane select “Configure Native Modules”:

Here the module is also missing

Now click “Register…”.

Add the module with name

SPRequestFilterModule

and Path

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\isapi\sprequestfilteringmodule.dll

Click on.

Try reloading the SharePoint page in browser. That’s it 🙂

I created this script to fix the issue:

$fn = "$([environment]::GetEnvironmentVariable("SystemRoot"))\system32\inetsrv\config\applicationhost.config"
$l = [System.Collections.Generic.List[string]]::new( ([system.io.file]::ReadAllLines($fn)) )

if( $null -eq ($l | ? { $_ -like "*SPRequestFilterModule*"} ) ) {
    Write-Host "SPRequestFilterModule not found..."

    if( (Test-Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\isapi\sprequestfilteringmodule.dll") ) {
        write-host "missing module found on disk!"

        . iisreset /stop

        $m = @()
        for( $i = 0; $i -lt $l.Count; $i++ ) {
            if( $l[$i].IndexOf("</globalModules>", [StringComparison]::OrdinalIgnoreCase) -ge 0) {
                $l.Insert($i, '            <add name="SPRequestFilterModule" image="C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\isapi\sprequestfilteringmodule.dll" preCondition="bitness64" />')
                break
            }
        }

        [system.io.file]::WriteAllLines($fn, $l.ToArray())

        . iisreset /start
    } else {
        Write-Error "Missing module DLL not found on disk!"
    }
} else {
    Write-Host "SPRequestFilterModule is already registered in IIS!"
}

PS: I found an article on Microsoft Learning. There I will leave a comment on that topic.

https://learn.microsoft.com/en-us/answers/questions/1228993/recent-sp-security-update-causes-iis-to-throw-a-50

PowerShell >= 7 : Skip Certificate Check for Win and Linux

Here is a snippet of how to disable certificate checks on PowerShell 7 and above on Windows and Linux / Debian 11.

if ($PSVersionTable.PSEdition -eq 'Core') {
	$Script:PSDefaultParameterValues = @{
        "invoke-restmethod:SkipCertificateCheck" = $true
        "invoke-webrequest:SkipCertificateCheck" = $true
	}
} else {
	Add-Type @"
		using System.Net;
		using System.Security.Cryptography.X509Certificates;
		public class TrustAllCertsPolicy : ICertificatePolicy {
			public bool CheckValidationResult(
				ServicePoint srvPoint, X509Certificate certificate,
				WebRequest request, int certificateProblem) {
				return true;
			}
		}
"@

	[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
}

PowerShell on Linux / Debian 11 – Ctrl+C not working

I just found out that on my Debian 11 VM in PowerShell the key combination Ctrl+C does not work.

The script is not terminated by this, unlike expected.

I added the following code and now it works.

add-type -typedefinition @"
using System;
using System.IO;

public class CtrlCHandler
{
	public static void Main()
	{
		System.Console.CancelKeyPress += (s,e) => System.Diagnostics.Process.GetCurrentProcess().Kill();
	}
}
	
"@

[CtrlCHandler]::Main()

Blank / Empty pages when Accessing a Modern Experience page of SharePoint Server Subscription Edition

Resently I installed a new SharePoint Server Subscription Edition but in this case only with the latest security update (https://support.microsoft.com/en-us/kb/5002191).

When I opened the home page of the newly created root site collection everything seems fine. But when navigation to some link in the quick launch I only got empty / blank pages. No errors in the Developer tools. Just no content.

(There was almost no content in the HTML DOM. Just the <head> tag with some content and the <body> tag with 1 <script> tag…)

After searching in the SharePoint logs I found this error:

Error encountered when creating uri from baseUrl /_layouts/15/next/odspnext/.

It helped me to find this page of Stefan GoĂźner: https://blog.stefan-gossner.com/2021/09/29/trending-issue-_layouts-15-viewlsts-aspx-shows-as-blank-page-in-sp2019-after-installing-september-pu/

This information is about ShgarePoint 2019 but the message and even the error code is identical.

So I installed the lastest available “language dependent” patch of January 2022: https://support.microsoft.com/en-us/kb/5002110

After installing it and after running the config wizard all the pages contents are shown!

SharePoint User Information List URL (REMINDER)

This is the link to the SharePoint User Information List:

https://sharepoint.example.com/_catalogs/users/simple.aspx

The bold part must be replaced by the SharePoint site collection URL.

The interesting part is this: /_catalogs/users/simple.aspx

For example:

https://intranet.yourcompany.com/sites/finance/_catalogs/users/simple.aspx

/sites/finance is the site collection part of the URL in this case.

This list is a special system list but it’s based on the “normal” list mechanism of SharePoint. Therefore it has an ID and by knowing that you can open the settings page of the list.

If you opend the User Information List using the URL above in Firefox, Edge, Chrome,… you can select the surrounding table and grab the list ID from the HTML DOM:

With this ID you can open the settings page:

https://sharepoint.example.com/_layouts/15/listedit.aspx?List={F9780EA0-8B18-47E1-88BF-7C9543561C58}

Microsoft Teams App: Open Developer Tools

As widely known, Microsoft Teams runs on desktop computers with Windows OS or Mac OSX or Linux. The “Teams” app is based on “Electron” (https://www.electronjs.org).

“Electron” runs a packaged web technology based app inside a Chromium based application.

Microsoft Teams is such a “web technology based app” that runs in such a Chromium based application locally on a computer.

Chromium normally offers “Developer Tools” to dig into the HTML / CSS / Javascriptg of a w web technology based application.

Normally the “Developer Tools” are disabled in “Microsoft Teams”.

But there is a trick to enable the “Developer Tools” on “Microsoft Teams”:

1. Windows

(Left) click 7 or more times on the “Teams” icon besides the clock.

Now right-click the icon once. There you see the Developer Tools:

2. Mac OSX

It’s almost the same with Mac OSX: There you click the “Teams” icon in the tray 7 times.

(I’ll add a screenshot later.)

3. Linux

Not tested.

ASP.Net Core .net 6 Demo Authentication Project using local Casdoor Docker Container on Windows Subsystem for Linux

I wanted to create a demo application with kind of real world authentication that I can easily adopt in projects.

This was not so easy as I thought.

1st: I wanted an identity provider that offers OAuth2.

2nd: I wanted the identity provider on my own machine or at least within my infrastructure.

3rd: I wanted a setup that I can describe to reproduce it.

Here is the result. – I work on Windows 11. It should work with Windows 10 too. – I use Visual Studio 2022.

Part 1: Preparation

Install Windows Subsystem for Linux version 2:

https://docs.microsoft.com/en-us/windows/wsl/install

Install Docker Desktop for Windows

https://docs.docker.com/desktop/windows/install/

Ensure you enabled WSL2 support in Docker!

Part 2: Setup

I need Casdoor as identity provider. It’s open source:

https://casdoor.org/

Pull the latest “all-in-one” Docker image of Casdoor.

docker pull casbin/casdoor-all-in-one

Now you create the container…

docker run -d -p  8000:8000 --name casdoor -v ./casdoor-data:/var/lib/mysql casbin/casdoor-all-in-one

Three comments on that:

  1. The Casdoor portal on your machine can be accessed using http://localhost:8000. If you need another port that change “8000:8000” to something else like “9000:8000”. The second port is internally used inside the Docker container. Do not change that. The first port is the published one on your machine.
  2. Casdoor is an identity provider. You will need to create identities in it. Of course you do not want to do that again and again. Therefore it’s a good idea to put the data of the Casdoor container into Docker volume. If you later recreate the container the volume will remain on disk.
  3. You ask: “Where is the Docker volume located on disk on WSL 2”? Good question! WSL creates a hidden mount point (?) that you can access on Windows by accessing \\wsl$ in the Explorer. Then you can navigate to the correct folder that contains the volume of Casdor: \\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes\casdoor-data

Now you can open Casdoor:

http://localhost:8000

The default login is: username “admin” with password “123” (without “”)

Now … create some users.

Then… create an application:

Part 3: The code

Now clone my github project:

https://github.com/ikarstein/com.kenaro.public.OAuth2Demo.Casdoor

Open the solution in Visual Studio 2022. Run it.

It will open a browser and looks like this:

Click “Authenticate using Casdoor”

Authenticate…

Thats it.

The magic happens in “Startup.cs”

.AddOAuth("casdoor", "Casdoor", options =>
        {
            options.AuthorizationEndpoint = "http://localhost:8000/login/oauth/authorize";
            options.TokenEndpoint = "http://localhost:8000/api/login/oauth/access_token";
            options.UserInformationEndpoint = "http://localhost:8000/api/userinfo";
            options.ClientId = "dc6556419364997a4032";
            options.ClientSecret = "2a4dbd07bbb655777a928ef99039a11d1e81d9d4";
            options.CallbackPath = "/signin-casdoor";
            options.ClaimsIssuer = "iss";
            options.SaveTokens = true;
            options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "name");
            options.ClaimActions.MapJsonSubKey(ClaimTypes.Gender, "data", "gender");
            options.ClaimActions.MapJsonSubKey(ClaimTypes.Name, "data", "displayName");
            options.ClaimActions.MapJsonSubKey(ClaimTypes.Email, "data", "email");
            options.ClaimActions.MapJsonSubKey(ClaimTypes.HomePhone, "data", "phone");
            options.ClaimActions.MapJsonSubKey(ClaimTypes.Locality, "data", "location");
            options.ClaimActions.MapJsonSubKey(ClaimTypes.Webpage, "data", "homepage");
            options.ClaimActions.MapJsonSubKey(ClaimTypes.Role, "data", "type");

            options.Events.OnCreatingTicket = async creatingTicketContext =>
            {
                var token = creatingTicketContext.Properties?.GetString(".Token.access_token");

                using var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:8000/api/get-account");
                request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);

                using var response = await creatingTicketContext.Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, creatingTicketContext.HttpContext.RequestAborted);
                if (!response.IsSuccessStatusCode)
                {
                    throw new HttpRequestException("An error occurred while retrieving the user profile from Authentik.");
                }

                var userInfo = await response.Content.ReadAsStringAsync(creatingTicketContext.HttpContext.RequestAborted);
                using var jsonDoc = JsonDocument.Parse(userInfo);
               
                creatingTicketContext.RunClaimActions(jsonDoc.RootElement);
            };
        });

I took a while to figure out how to configure the OAuth2 provider.

Manager Attribute not Updated in AD Import for Profiles on SharePoint Server On-Premises

Today I could solve a wired problem. It belongs to User Profile “AD Import” on SharePoint Server 2019.

The profile overview of a person stated two different information: At the top there was the correct department but in the organizational chart the person had the wrong manager and so a wrong department information.

The organizational chart is generated from the “Manager” attribute of the SharePoint user profile.

In the profile I saw a correct “department” attribute value but a wrong “manager” attribute value.

I started a “Full AD Import”.

In the SharePoint log I saw an exception with the message, that a XML file in the SharePoint timer cache on one server could not be changed.

So first I refreshed all timer cache folders on all servers: Stop Windows service “sptimerv4” on every server. Find the subfolder with file “cache.ini” below c:\programdata\microsoft\sharepoint\config. The folder has a GUID as name. Than delete all file but cache.ini. Than set the content of cache.ini to “1” (without “”) and restart the service “sptimerv4”. The cache folder gets filled again. The content of “cache.ini” will be set to a valid value…

Next I restarted AD Import using PowerShell:

$s = get-spserviceapplication <guid>
$s.StartImport($true)

Now I saw the sync working in the SharePoint log but the manager attribte was not updated still.

My next idea was that maybe the property mapping had a problem. I looked at the mapping config but it looked OK in the editor.

Out of curiosity I have removed the assignment for “manager”. Than I ran the import and checked the user profile afterwards.

As a big suprise for me the manager attribte now got updated. WITHOUT MAPPING.

It seems that the manager attribute is mapped internally.