Issue with PowerShell Remote Endpoints after a Windows 10 Upgrade

  • Context:

I’ve tested recently an upgrade to the latest branch 1803 (aka Spring Creators Update / RS4) of a Windows 10 Enterprise x64 that had a custom remote endpoint configuration.

  • Issue:

Everything was still there. It was still registered, its ACL still set… except that it doesn’t work.
If you attempt to create a new PSSession or use Enter-PSSession or Invoke-Command, it failed with the same error message:

New-PSSession : [localhost] Connecting to remote server localhost failed with the following error message : The WS-Management service cannot process the operation. An attempt to create a virtual account failed. Ensure that WinRM service is running as Local System and that it has TCB privilege enabled. For more information, see the about_Remote_Troubleshooting Help topic.

TCB means Trusted Computing Base. The WinRM service seems to have lost its (SeTcbPrivilege) ability to Act as part of the operating system and cannot create the virtual account used by the remote endpoint.

If I check the services on a 1709, I’ve:

If I check the services on a 1709 upgraded to a 1803, I’ve:

  • Solution 1:

Unregister and register

  • Solution 2:

There are two ways to restore the TCB privilege: either using sc.exe or by setting the RequiredPrivileges REG_MULTI_SZ value in the registry.

# using sc.exe to restore privilege
sc.exe privs WinRM SeAssignPrimaryTokenPrivilege/SeAuditPrivilege/SeChangeNotifyPrivilege/SeCreateGlobalPrivilege/SeImpersonatePrivilege/SeTcbPrivilege
# or directly setting the value in the registry
$HT = @{
 Path = 'HKLM:\SYSTEM\CurrentControlSet\Services\WinRM'
 Name = 'RequiredPrivileges'
 Value = @(
  'SeAssignPrimaryTokenPrivilege',
  'SeAuditPrivilege',
  'SeChangeNotifyPrivilege',
  'SeCreateGlobalPrivilege',
  'SeImpersonatePrivilege',
  'SeTcbPrivilege'
 )
}
Set-ItemProperty -Type MultiString @HT -Force

# Restore the local system account
Get-CimInstance -ClassName Win32_Service -Filter "Name='WinRM'"| 
Invoke-CimMethod -MethodName Change -Arguments @{StartName='LocalSystem'}

# Apply changes
Restart-Service -Name WinRM
  • Misc.:

Can you notice any other difference left by the upgrade?

The DisplayName was changed and the ServiceType was changed from 0x10 (it has its own process) to 0x20 (it’s a shared process).
Don’t ask me why the upgrade changed these settings? ๐Ÿ™„ I have no idea.

Bonus: Solution 1 is better than solution 2 because it also fixes the service type.

Advertisements

Detect and correct orphaned ‘adminCount=1’ in Active Directory

I recently needed to detect and correct orphaned ‘adminCount=1’ in Active Directory.
I googled that old problem and found the following code: https://gist.github.com/webash/b34c5a422288827ff4e53318e34c6923

I loved his honesty and the fact that he respected the best practice of adding a note where he has acquired the code.
The help also references a blog post on technet.

I don’t like to reinvent the wheel and I couldn’t resist fixing the code anyway.

Register a remote endpoint with DSC

  • Context:

I’ve been using Desired State Configuration (DSC) recently on a Windows 10 computer to create a custom remote endpoint configuration.

  • Issues:

Using the Register-PSSessionConfiguration with a -Force switch parameter breaks the push.
The error message says:
The WS-Management service cannot process the operation. The operation is being attempted on a client session that is unusable. This may be related to a recent restart of the WS-Management service. Please create a new client session and retry the operation if re-executing the operation does not have undesired behavior.
+ CategoryInfo : InvalidOperation: (root/Microsoft/…gurationManager:String) [], CimException
+ FullyQualifiedErrorId : HRESULT 0x803381fa
+ PSComputerName : localhost

If you specify a -Verbose switch parameter when you push the configuration with Start-DscConfiguration cmdlet, the verbose stream of the Register-PSSessionConfiguration cmdlet is still streamed although it’s explicitly turned off.

  • Solution:

The solution consisted in:

  1. turning off the verbose stream from the Register-PSSessionConfiguration cmdlet by enclosing this cmdlet in scriptblock executed by the Start-Job cmdlet.
  2. removing the -Force switch parameter that restarts and breaks the push
  3. using a -NoRestartService switch parameter with the Register-PSSessionConfiguration cmdlet

Create an ASCII encoded file with DSC

  • Context:

I’ve been using Desired State Configuration (DSC) recently to create a cmd file on every users’ desktop.

  • Issue:

DSC creates a UTF8 file with a BOM. The file is executed but not its first line. The File DSC resource doesn’t allow to specify an encoding

  • Solution:

I’ve used a File resource to create the content and a dependent child script resource that removes the BOM.

# Remove BOM because File DSC resource creates a UTF8 file with BOM
[System.IO.File]::WriteAllLines(
  'C:\Users\Public\Desktop\myCmdFile.cmd',
  (Get-Content -Path 'C:\Users\Public\Desktop\myCmdFile.cmd'), 
  (New-Object System.Text.UTF8Encoding($False))
)

Quick tip: Testing volume licensing activation by a KMS

To test the volume licensing activation by a KMS (Key Management Server), you can actually do it either on the KMS server(s) or on the computer to be activated.

On a KMS server, you’d typically do:

$HT = @{
 LogName = 'Key Management Service' 
 Id =12290 
 Data = "$($TargetComputerNameFQDN)"
}
Get-WinEvent -FilterHashtable $HT -ErrorAction SilentlyContinue | 
ForEach-Object {
 [PSCustomObject]@{
  Result = ($_.Properties)[1].Value
  'Minimum count needed to activate' = ($_.Properties)[2].Value
  'KMS client FQDN' = ($_.Properties)[3].Value
  'Client Machine ID (CMID)' = ($_.Properties)[4].Value
  'Client TimeStamp' = ($_.Properties)[5].Value
  'is client VM?' = [bool]($_.Properties)[6].Value
  'License State' = ($_.Properties)[7].Value
  'Time State to expiration (min)' = ($_.Properties)[8].Value
  GUID = ($_.Properties)[9].Value
 }
}  | 
Out-GridView

NB1: I found the explanation of the values on this page
NB2: the license state value 1 means activated. 2 means OOB grace. 5 means there was a problem between the time of the KMSHost and the client (>4hours)

On a client KMS computer, you’d do:

Get-CimInstance -Query "Select * FROM SoftwareLicensingProduct WHERE ApplicationId = '55c92734-d682-4d71-983e-d6ec3f16059f' AND LicenseFamily = 'Enterprise' AND Description LIKE'%VOLUME_KMSCLIENT%'" |
Select Description,ApplicationId,LicenseStatus


NB1: In the above example, I’m looking for a Windows 10 Enterprise version. Notice the LicenseStatus set to 1 (which means it’s activated).

Quick tip: What new products and classifications have been added in the past 30 days to WSUS?

Two days ago a PM.org list member asked the following question:

When you see the notice โ€œX new products and Y new classifications have been added in the past 30 daysโ€, how do you know which ones are new when you click on the link.

The only answer was a piece of PowerShell code ๐Ÿ˜€ that another list member pointed (here)

# Get new classifications
(Get-WsusServer).GetUpdateClassifications(
 "$((Get-Date).AddDays(-31))",
 "$(Get-Date)"
) | Select Title,Description, Releasenotes, arrivaldate

# Get what new products were added over the month
(Get-WsusServer).GetUpdateCategories(
 "$((Get-Date).AddDays(-31))",
 "$(Get-Date)"
) |  
Select Title,Id,arrivaldate | 
ft -AutoSize

Enjoy!

About updating inbox PowerShell modules

I’ve started testing Windows 10 1803 branch and got surprised by a few things.

First issue: Update-Module : Module ‘Pester’ was not installed by using Install-Module, so it cannot be updated

I wanted to get the latest version of Pester and naรฏvely ran:

Update-Module -Name Pester -Verbose

Ok, I hardly remember other MVPs talking about this problem…
To fix it ASAP, I did:

# Step 1: Save locally
Find-Module -Name Pester -Repository PSGallery -Verbose | 
Save-Module -Path  ~/Downloads -Verbose

# Step 2: remove alternate stream if any
dir ~/Downloads/Pester/* -rec -for -ea 0 | 
Unblock-File -Verbose

# Step 3: move to Programfiles
gi ~/Downloads/Pester/* | 
Copy-Item -Destination "C:\Program Files\WindowsPowerShell\Modules\Pester" -Recurse

NB: I moved it to programfiles because the Get-Module uses the PSModulePath environment variable to find and enumerate available modules. Keep that in mind. I’ll come back later on this.

The above longer way will actually prevent you from hitting a second road block if you try to take a shortcut.
Note that it’s also a best practice to review downloaded code before installing and trusting it.

Second issue:
PackageManagement\Install-Package : The version ‘4.3.1’ of the module ‘Pester’ being installed is not catalog signed.
Ensure that the version ‘4.3.1’ of the module ‘Pester’ has the catalog file ‘Pester.cat’ and signed with the same
publisher ‘CN=Microsoft Root Certificate Authority 2010, O=Microsoft Corporation, L=Redmond, S=Washington, C=US’ as
the previously-installed module ‘4.3.1’ with version ‘3.4.0’ under the directory ‘C:\Program Files\WindowsPowerShell\Modules\Pester\3.4.0’. If you still want to install or update, use -SkipPublisherCheck

Updating the module doesn’t work because it’s not installed from the PSGallery and is instead shipped inbox.
Let’s install it using Install-Module and not Update-Module. Right?

Find-Module -Name Pester -Repository PSGallery -Verbose | 
Install-Module -Verbose


Notice the above warning about the untrusted source.

Ok, don’t give up or be irritated, the solution and the shortcut is in the red error message ๐Ÿ˜›

Install-Module -Name Pester -Repository PSGallery -SkipPublisherCheck

One last step, you’re almost there. This time the fix is in the warning stream. You need a -Force.

Install-Module -Name Pester -SkipPublisherCheck -Force -Repository PSGallery 

With the 2nd issue illustrated, you probably understand why I go my way (using the 3 steps shown in issue #1 above)

Third issue: The installed module doesn’t show up!

No matter what I do, the installed module isn’t discovered by the Get-Module cmdlet although it’s located in one of the locations stored in the $env:PSModulePath variable:

Get-Module -Name Pester -ListAvailable
Get-Module -Name Pester -ListAvailable -Refresh
$env:PSModulePath -split ';'

The PowerShell prompt is elevated and running in constrained mode.

$ExecutionContext.SessionState.LanguageMode


Applocker Allow mode (no default rules!) is preventing the new module from being discovered.
Why?
Because of the digital signature. The 3.4.0 is signed by Microsoft and the 4.3.1 isn’t signed at all

Get-AuthenticodeSignature 'C:\Program Files\WindowsPowerShell\Modules\Pester\*\*.ps?1'

How to fix this?
If you can, the best solution is to use an internal PKI and/or a code signing certificate to sign the 4.3.1 files. Then you just add a single Applocker rule to trust that Publisher.
There’s a second solution.

Fourth issue: an Applocker cmdlet shortcoming…
When you use the -Directory parameter, it doesn’t pick .psd1 and .psm1 files ๐Ÿ˜ฆ

Get-AppLockerFileInformation -Directory 'C:\Program Files\WindowsPowerShell\Modules\Pester\4.3.1' -Recurse -FileType Script


You can for sure use the Get-AppLockerFileInformation cmdlet on PSD1 and PSM1 files

# Check psd1 and psm1 files
Get-AppLockerFileInformation 'C:\Program Files\WindowsPowerShell\Modules\Pester\4.3.1\*.ps?1'

# Try to create a policy
Get-AppLockerFileInformation 'C:\Program Files\WindowsPowerShell\Modules\Pester\4.3.1\*.ps?1' |
New-AppLockerPolicy -User EveryOne


You can pipe the two cmdlets Get-AppLockerFileInformation and New-AppLockerPolicy but it creates an empty Applocker policy object ๐Ÿ˜ฆ

Here’s my solution:

# Copy psd1 & psm1 files as ps1 files
dir 'C:\Program Files\WINDOWSPOWERSHELL\MODULES\PESTER\4.3.1\*.ps?1' -Recurse -Force | 
Foreach-Object { 
 $_ | Copy-Item -Destination "$($_.FullName).ps1"  
}

# Create a local policy using these 2 files
Get-AppLockerFileInformation -Directory 'C:\Program Files\WindowsPowerShell\Modules\Pester\4.3.1' -Recurse -FileType Script | 
New-AppLockerPolicy -User EveryOne -RuleNamePrefix 'Allow Pester 4.3.1' | 
Set-AppLockerPolicy -Merge -Verbose

Last but not least. Refresh the group policies.

Now, Pester 4.3.1 can be imported and used even in ConstrainedLanguage mode ๐Ÿ˜Ž