Fix Calculator in Windows 10: “You’ll need a new app to open this calculator”

Microsoft had to go and reinvent the wheel, and replace good ‘ole calc.exe in Windows 10 since late 2017. I can see why they did it – to make it touch-friendly.

I’ve seen an error that prevents the new calculator app from even loading in the first place. I experienced that error on my own machine after clearing out my Windows profile and logging on fresh:


I can’t work without a calculator app, I use it all the time, so I had to set off and try to find a solution. There are all sorts of involved solutions out there, but what worked for me was as simple as these two lines of PowerShell (run as an admin):

Get-AppxPackage -Name Microsoft.WindowsCalculator | Remove-AppxPackage


Get-AppxPackage -Name Microsoft.WindowsCalculator | Add-AppxPackage

After that, my calculator app started working again:

2018-04-12 13_59_15-Calculator

Note: This post has been sitting in my drafts folder for almost a year, waiting for an additional screenshot. I decided to publish it today, but there may now be better solutions to this problem.

Using Azure Blob Storage as a highly-available CDP and AIA location for your internal PKI

I inherited a Windows PKI setup that had the Root CA installed on a Windows Server 2008 R2 Domain Controller, with the root certificate signed with a SHA1 hash. That DC was in the process of being decommissioned, and I also wanted to move to a better PKI design.

I’d previously set up 2-tier Windows PKI infrastructures with offline Root CAs, so I knew that this was the route I was going to take again (note that this is for an SMB environment).

There are plenty of good guides on configuring a 2-tier Windows PKI. In my opinion the best of the crop at the time of writing is probably Timothy Gruber’s 7-part guide to deploying a PKI on Windows Server 2016.

I would, however, highly recommend reading up on the topic before blindly following a guide. PKI is a complex topic, and you want to make the correct decisions up-front to avoid issues later on. Some additional recommended reading:

There are many recommendations around where to publish/advertise the AIA and CDP. Some of these include:

  • In the default location – LDAP and locally via HTTP on the CA server
  • To an internally-hosted web server, and then reverse-proxy connections from the Internet
  • To an externally-hosted web server

I’d already used Azure Blob Storage to store some other small files, so I thought I’d have a go at seeing if it’s able to be used for AIA and CDP storage. As it turns out, it’s quite easy to do, and you don’t even need to mess around with double-escaping like you would need to if you hosted on IIS or an Azure Web App:

TLDR; The CA saves the CRL files to the default location of C:\Windows\System32\CertSrv\CertEnroll, and AzCopy then copies them up to an Azure Blob Storage account that’s configured with a custom domain of

Here are the requirements to get this all set up:

  1. CDP and AIA on Enterprise/issuing CA configured to save to the default C: location, and also advertise availability at
  2. AzCopy installed on the Enterprise CA
  3. Allow outbound HTTPS/443 from the Enterprise CA to Azure Blob Storage
  4. An Azure Storage Account with blob storage configured for HTTP access. I’d recommend at least Zone Redundant Storage for availability.
  5. A custom domain name for the above storage account
  6. A folder in the blob storage named ‘pki’ (not necessary, but you’ll need to adjust the script if you don’t use this folder)
  7. A SAS key with read/write/change access to blob storage only (don’t assign more access than necessary)
  8. A scheduled task running hourly as NETWORK SERVICE to call the below PowerShell script
  9. Ensure that NETWORK SERVICE has modify permissions to the log location (default is %ProgramData%\ScriptLogs\Invoke-UpdateAzureBlobPKIStorage.log)

You’ll need to manually copy your offline root CA certificate and CRL to the blob storage location. This script is designed to copy the much more frequent CRLs and Delta CRLs from your Enterprise CA to blob storage.

As it turns out, AzCopy is perfect for this because it supports the /XO parameter to only copy new files. That allows us to schedule the script to run hourly without incurring additional data transfer costs for files that already exist in the storage account.

I wrote a PowerShell script that does the following:

  1. Checks that AzCopy is installed
  2. Determines if the C:\Windows\System32\CertSrv\CertEnroll folder exists
  3. Copies only changed files with extension .CRL to to the blob storage account
  4. Logs successful and failed transfers to %ProgramData%\ScriptLogs\Invoke-UpdateAzureBlobPKIStorage.log

You can find the script on my Github repo here:


Use pkiview.msc on a domain-joined machine to check the status of your CDP and AIA


Generating a SAS with least-privilege for AzCopy to use. Note that you’ll need to set Allowed Protocols to HTTPS and HTTP, not HTTPS only


The script’s archive log, showing the successful transfer of the CRL and Delta CRL

As always, use this at your own risk and your mileage may vary. Please drop me a comment below if you have any questions, feedback, or run into issues with the script.

Reset the CSC (Offline Files) database

Over the years I’ve had many issues where a Windows client PC just won’t connect to a share using the FQDN, but can connect using the NetBios name. There have also been plenty of occasions where the opposite is true, too.

I had the issue again today on a freshly-built Windows 10 machine. Folder redirection wouldn’t apply because the user’s desktop folder was inaccessible.

Then I came across this post on the Spiceworks forums that mentioned resetting the CSC database. I tried that, and folder redirection now works perfectly!

If caching is enabled on the share in question, and the CSC database is knackered, you’ll run into this same problem.

If only I’d known about this earlier in my 15-year IT career, it could potentially have saved a lot of headaches.

Add the following reg key/value. then reboot the PC:

reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Csc\Parameters /v FormatDatabase /t REG_DWORD /d 1 /f

Script to create new local admin account for use with LAPS

I’ve got a bunch of older SOE machines that still had the local Administrator account enabled. As part of implementing Microsoft LAPS, I wanted to disable that account, and use a newly-created ‘LocalAdmin’ account with LAPS.

The account is created with a randomly-generated GUID as the password. The account’s password is going to come under the management of LAPS anyway. Additionally, it would be a terrible idea to hard-code a password into a script that’s stored in Sysvol.

If an account with that name already exists, the script will quit. Some basic events are also logged to the Event Log to indicate what happened.

My first revision of the script used ADSI to create the account and add it to the Administrators group, but I found that my mileage varied with that method. Some computers had the account created, but it wasn’t a member of Administrators.

It’s now set up to use plain “NET USER” and “NET LOCALGROUP” commands. This is an example of what would be executed:
2016-04-04 16_01_06

This script is designed to be set up as a computer Startup Script:

# The name of the account
$accountName = 'LocalAdmin'
$accountFullName = 'Local Administrator'
$accountComment = 'Backup Local Administrator Account'
# Any users listed here will be disabled by this script
$usersToDisable = 'Administrator','Guest'
# Set up some Event Log stuff
$sourceName = "$($MyInvocation.MyCommand.Name).ps1"
New-EventLog LogName Application Source "$sourceName" ErrorAction SilentlyContinue WarningAction SilentlyContinue
# If the account already exists, exit
if ((Get-WmiObject Win32_UserAccount filter "domain = '$Env:COMPUTERNAME' and Name = '$accountName'") -ne $null) {
Write-EventLog LogName Application Source $sourceName EntryType Information EventId 1 Message "$accountName already exists" ErrorAction SilentlyContinue WarningAction SilentlyContinue
# Create the account
cmd.exe /c "net user $accountName `"$([guid]::NewGuid().guid)`" /add /y /comment:`"$accountComment`" /fullname:`"$accountFullName`""
# Add the account to the Administrators group
cmd.exe /c "net localgroup Administrators $accountName /add"
# Disable the specified users
$usersToDisable | Foreach-Object {cmd.exe /c "net user $_ /active:no"}
# Try and write an event to the Event Log
Write-EventLog LogName Application Source $sourceName EntryType Information EventId 2 Message "Created local administrator account: $accountName" ErrorAction SilentlyContinue WarningAction SilentlyContinue

Create a Scheduled Task to keep all Chocolatey Packages up-to-date

Rebuilding your PC is always a drag, even with useful utilities like Ninite.

I recently created a PowerShell DSC script that I can use whenever I need to rebuild my PC. As part of that, I used the cChoco provider to automatically install applications using Chocolatey. I’ll be writing a blog post with more details shortly.

That’s a great way to get the applications installed, but not for keeping them up-to-date. Chocolatey allows you to run ‘choco upgrade all’ manually to do this:

Command prompt window showing the results of a 'choco upgrade all -y' command

Rather than manually create the scheduled task to automate this, I created this short PowerShell script:

# See if choco.exe is available. If not, stop execution
$chocoCmd = Get-Command Name 'choco' ErrorAction SilentlyContinue WarningAction SilentlyContinue | Select-Object ExpandProperty Source
if ($chocoCmd -eq $null) { break }
# Settings for the scheduled task
$taskAction = New-ScheduledTaskAction –Execute $chocoCmd Argument 'upgrade all -y'
$taskTrigger = New-ScheduledTaskTrigger AtStartup
$taskUserPrincipal = New-ScheduledTaskPrincipal UserId 'SYSTEM'
$taskSettings = New-ScheduledTaskSettingsSet Compatibility Win8
# Set up the task, and register it
$task = New-ScheduledTask Action $taskAction Principal $taskUserPrincipal Trigger $taskTrigger Settings $taskSettings
Register-ScheduledTask TaskName 'Run a Choco Upgrade All at Startup' InputObject $task Force

The script will:

  1. Locate the choco.exe binary (It’ll quit if it can’t find it in the path)
  2. Set up a scheduled task that runs said binary at system startup

Note that this script will only work on Windows 8 and newer machines, because it relies on the *-ScheduledTask cmdlets.

Reading the Windows DHCP Server logs with PowerShell

I’ve been doing a bit of work with DHCP over the last week or so – specifically with troubleshooting IP assignment from various VLANs. I threw together a quick function to read the last (x) lines out of the current day’s DHCP server log. For now, there’s no support for reading the logs remotely. This needs to be run on the server itself.

Once you’ve dot sourced the script, just call the function. By default, it will grab the last 20 lines out of the current day’s log file.


You can specify a day, and/or the number of lines to grab from the end of the log file:

Get-DHCPServerLog -Lines 5 -Day mon

You can also pipe it to Select-Object:

Get-DHCPServerLog | Select-Object Date,Time,Description,MAC*,IP* | Format-Table -AutoSize

Here’s the source:

#requires -version 3
Get-DhcpServerLog – Reads the Windows DHCP server logs
The Windows DHCP server logs are stored in CSV format in C:\Windows\System32\dhcp
It's difficult to read these logs in Notepad due to them being in CSV format.
By default, this script reads the last 20 lines of the current day's log, and
converts each line into a PSObject.
Additionally, the PSObject will also contain descriptive versions of the ID and QResult fields
A PSObject, output from ConvertFrom-CSV
Get-DhcpServerLog | Select-Object Date,Time,Description,MAC* | Format-Table -AutoSize
Date Time Description MAC Address
—- —- ———– ———–
08/14/15 10:23:46 Renew 68F7286B71AA
08/14/15 10:25:38 Database Cleanup Begin
08/14/15 10:25:38 Expired
08/14/15 10:25:38 0 leases expired and 0 leases deleted
08/14/15 10:25:38 0 leases expired and 0 leases deleted
08/14/15 11:37:52 Renew AC162BF8249C
Written By: Daniel Streefkerk
Todo: Nothing at the moment
Change Log
v1.0, 14/08/2015 – Initial version
function Get-DhcpServerLog {
[int]$Lines = 20,
[ValidateSet("mon","tue", "wed", "thu", "fri", IgnoreCase=$true)]
[string]$Day= (get-date).DayOfWeek.ToString().Substring(0,3)
Write-Verbose "Get-DHCPServerLog called with parameters – Lines:$Lines, Day:$Day"
# CSV header fields, to be used later when converting each line of the tailed log from CSV
$headerFields = @("ID","Date","Time","Description","IP Address","Host Name","MAC Address","User Name","TransactionID","QResult","Probationtime","CorrelationID","Dhcid","VendorClass(Hex)","VendorClass(ASCII)","UserClass(Hex)","UserClass(ASCII)","RelayAgentInformation","DnsRegError")
# Translations of the ID field, as per the description inside the log file itself
$idMeanings = @{
00 = "The log was started.";
01 = "The log was stopped.";
02 = "The log was temporarily paused due to low disk space.";
10 = "A new IP address was leased to a client.";
11 = "A lease was renewed by a client.";
12 = "A lease was released by a client.";
13 = "An IP address was found to be in use on the network.";
14 = "A lease request could not be satisfied because the scope's address pool was exhausted.";
15 = "A lease was denied.";
16 = "A lease was deleted.";
17 = "A lease was expired and DNS records for an expired leases have not been deleted.";
18 = "A lease was expired and DNS records were deleted.";
20 = "A BOOTP address was leased to a client.";
21 = "A dynamic BOOTP address was leased to a client.";
22 = "A BOOTP request could not be satisfied because the scope's address pool for BOOTP was exhausted.";
23 = "A BOOTP IP address was deleted after checking to see it was not in use.";
24 = "IP address cleanup operation has begun.";
25 = "IP address cleanup statistics.";
30 = "DNS update request to the named DNS server.";
31 = "DNS update failed.";
32 = "DNS update successful.";
33 = "Packet dropped due to NAP policy.";
34 = "DNS update request failed as the DNS update request queue limit exceeded.";
35 = "DNS update request failed.";
36 = "Packet dropped because the server is in failover standby role or the hash of the client ID does not match.";
# Event descriptions for 50-64 sourced from
50 = "The DHCP server could not locate the applicable domain for its configured Active Directory installation.";
51 = "The DHCP server was authorized to start on the network.";
52 = "The DHCP server was recently upgraded to a Windows Server 2003 operating system, and, therefore, the unauthorized DHCP server detection feature (used to determine whether the server has been authorized in Active Directory) was disabled."
53 = "The DHCP server was authorized to start using previously cached information. Active Directory was not currently visible at the time the server was started on the network.";
54 = "The DHCP server was not authorized to start on the network. When this event occurs, it is likely followed by the server being stopped.";
55 = "The DHCP server was successfully authorized to start on the network.";
56 = "The DHCP server was not authorized to start on the network and was shut down by the operating system. You must first authorize the server in the directory before starting it again.";
57 = "Another DHCP server exists and is authorized for service in the same domain.";
58 = "The DHCP server could not locate the specified domain.";
59 = "A network-related failure prevented the server from determining if it is authorized.";
60 = "No Windows Server 2003 domain controller (DC) was located. For detecting whether the server is authorized, a DC that is enabled for Active Directory is needed.";
61 = "Another DHCP server was found on the network that belongs to the Active Directory domain.";
62 = "Another DHCP server was found on the network.";
63 = "The DHCP server is trying once more to determine whether it is authorized to start and provide service on the network.";
64 = "The DHCP server has its service bindings or network connections configured so that it is not enabled to provide service."
$qResultMeanings = @{0 = "No Quarantine"; 1 = "Quarantine"; 2 = "Drop Packet"; 3 = "Probation"; 6 = "No Quarantine Information"}
$filePath = "$env:SystemRoot\System32\dhcp\DhcpSrvLog-$Day.log"
Write-Verbose "Attempting to search for DHCP log at location: $filePath"
if ((Test-Path $filePath) -eq $false) { throw "Couldn't locate DHCP log at $filePath" }
Write-Verbose "Reading last $Lines lines from DHCP log at location: $filePath"
Get-Content $filePath tail $Lines | ConvertFrom-Csv Header $headerFields | Select-Object *,@{n="ID Description";e={$idMeanings[[int]::parse($_.ID)]}},@{n="QResult Description";e={$qResultMeanings[[int]::parse($_.QResult)]}}

Braindump: ADDS NTP Configuration via GPO

There’s a great article on the Microsoft AD team blog about configuring the authoritative time server automatically via group policy and WMI filters. This may save you from domain time sync issues if your PDC emulator role eventually ends up moving to a different server.

Their article covers how to set up the WMI filter, but doesn’t address the settings for NTP. Those are listed in detail under this support note.

These are the settings I’ve implemented in my GPO using Admin Templates->System->Windows Time Service:

Windows Registry Editor Version 5.00