WordPress on IIS

These are my notes for getting WordPress on IIS 10 (1809) on Windows Server 2019 Core working.  I ran into a problem with MySQL 8.0 so I reverted back to MySQL 5.6 until I can spend a little more time troubleshooting and document my experience.

[This is a running draft currently]

  1. Install and Configure: IIS 10 (1809)
    1. IIS Role
      1. Features > CGI, Dynamic Compression
    2. Configure IIS FastCGI parameters – can be done with Administration pack, but I’m running Core and did from commandline.
      1. %windir%\system32\inetsrv\appcmd set config -section:system.webServer/fastCgi /[fullPath='c:\{php_folder}\php-cgi.exe'].instanceMaxRequests:10000
      2. %windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/fastCgi /+"[fullPath='C:\{php_folder}\php-cgi.exe'].environmentVariables.[name='PHP_FCGI_MAX_REQUESTS',value='10000']"
    3. GZIP (static/dynamic compression)
  2. Install and Configure: PHP 7.3
    1. Non-thread safe PHP 7.3 x64 installed to C:\PHP
    2. Needs VC15 redistributable (x64)
    3. Configure date.timezone, upload_tmp_dir, etc.
      1. Set permissions for tmp file location using icacls
        1. icacls tmppath /grant "IIS_IUSRS":(OI)(CI)F
  3. Install and Configure: MySQL Community Server 5.6.42
    1. Needs VC13 redistributable
    2. Configure bind-address for in C:\ProgramData\MySQL\my.cnf
  4. Install and Configure WordPress
    1. Folder permissions required (IIS_IUSR) using icacls
      1. icacls websiteroot /grant "IIS_IUSRS":(OI)(CI)F
    2. Download latest ZIP
      1. Tip: PowerShell Extract-Archive latest.zip works great – seemed slow, but it worked.
    3. IIS Rewrites (2.1 is required – I used x64 download)
      1. Permissions
        1. Note:  example.com is the name of  a website I created in IIS, so permissions use the App Pool Identity.
        2.   icacls "C:\inetpub\example.com" /grant:r "IIS APPPOOL\example.com:(OI)(CI)(RX,W)" /T
        3. Some notes I need to revise:
          1. iis permissions setup
            icacls PATH /remove "UserOrGroup" /t (removes recursively)
            i use:
            icacls httproot /inheritance:d
            icacls httproot /remove "BUILTIN\Users" /t
            icacls httproot /grant "IIS APPPOOL\domain.com" (this will propagate via inheritance to httproot\*)
            icacls httproot /grant "IIS APPPOOL\domain.com:(OI)(CI)F" /t
            PS C:\inetpub\domain.com> icacls httproot
            httproot IIS APPPOOL\domain.com:(F)
            NT SERVICE\TrustedInstaller:(F)
            NT SERVICE\TrustedInstaller:(OI)(CI)(IO)(F)
            CREATOR OWNER:(OI)(CI)(IO)(F)
            create site
            authentication > anonymouse authentication > edit > change to Application Pool Identity > OK
            right click on site > manage > advanced > ensure application pool is selected for correct application pool (domain.com)
            application pools > right click domain.com pool > ensure Process Model section > Identity is set to ApplicationPoolIdentity
  5. LetsEncrypt SSL
  6. benchmark
  7. Php 5.7 on old iis 7.5 (2008R2) vs new server (2019 core) with php 7.3


Error: This operation was blocked by role based access control settings or other network issue

I’m attempting to upload a file via Windows Admin Center and encountered the following error: Error: This operation was blocked by role based access control settings or other network issue.

Cursory search points to an issue with role-based access, so within Windows Admin Center dashboard, I hit the settings (bottom left) and choose Role Based Access Control.

In here, I attempt to apply Role Based Access Control (RBAC) and get the following error.

Couldn’t apply role-based access control to the computer. Error: The network path was not found.

Another cursory search leads me to think, via Windows Admin Center known issues, that it could be the Windows Defender Application Control (WDAC).  So for kludge purposes, I just decide to remove Windows Defender itself;  I’ll be utilizing a different security suite anyway whenever this makes it to production.

To remove Windows Defender completely from Server 2019 using powershell, I use the following command: Uninstall-WindowsFeature -Name Windows-Defender

Note:  This will require a reboot of your server.

After my server came back up, I again tried to apply Role Based Access Control and it failed with the same error.  Now I will dig in a little deeper and update this post soon with new information and hopefully a solution I’ve found.


The firewall was the culprit — disabling it, Set-NetFirewallProfile -Profile domain,public,private -Enabled false, resolved the upload error.  I have not attempted to apply RBAC yet.

Tip:  Ensure PSRemoting is enabled on the target server as well as firewall rules are added.  From an elevated PowerShell prompt:


Set-NetFirewallRule -Name WINRM-HTTP-In-TCP-PUBLIC -RemoteAddress Any

I also needed to allow port 445 as those two didn’t solve my issue:

New-NetFirewallRule -DisplayName “WAC File Upload” -Direction Inbound -Action Allow -Protocol TCP -Port 445 -RemoteAddress -Profile Public,Domain,Private

The next error I get, and I’m not sure why yet, is the following AFTER uploading a file.  The file does upload and the file content is good, but I get the following notification error.

Failed to upload to C:\PHP\TEST. Error: QueryCache: Unable to refresh data; call createObservable() and fetch() first.

Cursory search shows not much info out there.  A couple GitHub repos with JSON file this string belongs in and some stuff for Excel / SQL linking.

When in doubt, procmon reboot.

Well, that didn’t work.  But uploads work, and I guess that’s good enough for Government work.

Ah wait.  I had a look at that error message again and it seemed “webbrowserish” (like jQuery or something) so I restarted my browser and I no longer get that error.


wget for Windows Server core

I’m setting up a new IIS role on a new install of Windows Server 2019 core and I needed to grab a file from a remote webserver.

My go-to for this would be the powershell alias wget. This is just an alias for Invoke-WebRequest. Problem, on Server Core is that it requires the Internet Explorer engine – which isn’t going to be available on Server Core.

One workaround is to use the -UseBasicParsing option.

invoke-webrequest -usebasicparsing https://example.com/download.zip -out download.zip

But I also found another neat trick – and this method is a LOT faster to download during my testing.

Import-Module BitsTransfer
Start-BitsTransfer -source "https://example.com/download.zip"

This will save the file in the working directory.

Use PowerShell to get processes greater than 1000 CPU(s) on remote server

Find out which processes are consuming 1 second CPU time.

Invoke-Command -ComputerName win2k8-01 -ScriptBlock { Get-Process | Where CPU -gt 1000 } -ErrorAction SilentlyContinue | Sort CPU -Descending | Format-Table -AutoSize
PS C:\WINDOWS\system32> Invoke-Command -ComputerName win2k8-01 -ScriptBlock { Get-Process | Where CPU -gt 1000 } -ErrorAction SilentlyContinue | Sort CPU -Descending | Format-Table -AutoSize

Handles NPM(K)  PM(K)  WS(K)    CPU(s)   Id SI ProcessName PSComputerName
------- ------  -----  -----    ------   -- -- ----------- --------------
   3395     35  28688  37512 14,120.66  504    lsass       win2k8-01
   1875      0    124    300 10,514.17    4    System      win2k8-01
   5552     91  94932 111780  3,009.01  828    svchost     win2k8-01
    832     21   8832  16596  1,654.25 1740    vmtoolsd    win2k8-01
    630     39  35396  45676  1,591.18 1096    spoolsv     win2k8-01
   1006     26  11548  18792  1,256.78 1292    svchost     win2k8-01
    748     79  61904  69492  1,019.92  988    svchost     win2k8-01
    724     46 228852 200448  1,006.57 1428    tomcat7     win2k8-01

Make Windows Photo Viewer the default image viewer on Windows 10

Windows 10 Creator update replaced (read: forced users to use) the Microsoft Photos app with Microsoft’s new Paint 3D. I didn’t like Microsoft Photos app, and I really dislike Paint 3D.

I found an article on Howto Geek and it provides a downloadable Registry tweak to actually bring back the older Windows Photo Viewer that most of us are familiar with from Windows 7.

Note: If you do not want the older Windows Photo Viewer and just prefer the Microsoft Photos app, you can install that app from the Microsoft Store.

I have also saved the download and have made it available from this server as well.

Windows Server 2019 Licensing Notes

Windows Server 2019 per-core licensing model requires a minimum of 8 cores per physical socket, with 16 total cores minimum licensed for a server.

Windows Server 2019 user CALs are required for every user accessing directly, or indirectly, the server.

Merry Christmas

Not sure who reads this 12 year old tech blog of mine, but I wish you a merry Christmas and I hope you have a happy, healthy and successful new year.

Case of Dead Path on ESXi

I had 8 paths go down to a dead state on an ESXi host.  The paths were MRU via Fiber Channel to a storage array.  One path worked and it was configured as RR path.

I knew this wasn’t a physical issue, it had to be a software/configuration issue on my host because there were:

  • No storage array errors
  • Additional hosts in the cluster had no problems
  • One path still worked from the HBA

Looking at the log (/var/log/vmkernel.log) I searched for one of the LUN identifiers, in my case “:L30” which was one of the dead paths.  This yielded a result showing an error with NMP plugin driver invalid command.

Next step was to figure out and verify what NMP details were and compare against a working host.

esxcli storage nmp device list |grep "Path Selection Policy:" |sort |uniq -c

I saw nothing out of the ordinary.

Apparently the storage did not like the use of RR so I removed the SATP claim rule though, so I removed it:

esxcli storage nmp satp rule remove -V IBM -M "^1746*" -P VMW_PSP_RR -s VMW_SATP_ALUA

Storage paths are happy now.

WP Preserve Backslashes

I created a WordPress plugin based on a personal dilemma I ran into with my site being stripped of backslashes.

Upon post save, it converts backslashes to HTML entity \ which is what will be stored in the database.

The plugin is available on GitHub at https://github.com/rjkreider/wp-preserve-backslashes

Here’s the function if you want to just drop it in your functions.php file instead of installing it as a plugin.

function wppb_keepbackslash($PostID) {
    $thePost = get_post($PostID);
    $Content = str_replace('\\', '\', $thePost->post_content);
    // unhook this function so it doesn't loop infinitely
    remove_action( 'save_post', 'wppb_keepbackslash' );
    $UpdatedPost = array (
          'ID'           => $PostID,
          'post_title'   => $thePost->post_title,
          'post_content' => $Content
  wp_update_post( $UpdatedPost );
/** if (is_wp_error($post_id)) {
 $post_id=   wp_update_post( $UpdatedPost );
        $errors = $post_id->get_error_messages();
        foreach ($errors as $error) {
                echo $error;
}   **/
    // re-hook this function
    add_action( 'save_post', 'wppb_keepbackslash' );
add_action('save_post', 'wppb_keepbackslash' ); // Update Content when saving content

All my backslashes are gone in WordPress. Yikes.

Discovered that my most recent conversion from SQLite to MySQL seems to have screwed up my backslashes in all my posts that have backslashes.

This is bad because my code snippets should not be copy & pasted and run at face value unless you verify the code!  It could seriously break shit.

Ugh.  This is going to be a PITA to go and fix 500 posts.   It might be quicker to try to fix the SQLite DB file and try another conversion.  This isn’t the first time I’ve noticed this problem.  I see the issue when I restore from XML files as well, and even just copying database using something like mysqldump to dump and then importing using mysql command.  I’m probabaly just missing a simple flag to not strip slashes or something.

My next step is to confirm if there is actually a backslash in the SQL data and it is just being stripped in the_content() or something;  or if the backslash is REALLY not there.  *sad face*

update 1:  the slashes are not in SQL.  Looks like I need to look at my export DB to see if they are in that. *crosses fingers*

update 2:  found this article that creates a function to convert backslashes into HTML entities as the posts are saved.  https://www.tweaking4all.com/web-development/wordpress/preserve-backslash-in-posts/#comment-268277