Fixing a WMI provider load failure

I’ve been recently working with Power options on Windows for another purpose and couldn’t believe when Hyper-V MVP Niklas Γ…kerlund reported on his blog that the Get-CIMInstance cmdlet on Windows 2012 R2 Core edition fails to read power plans 😦

gcim -N root/cimv2/power -Class Win32_PowerPlan

Get-CimInstance : Provider load failure
+ FullyQualifiedErrorId : HRESULT 0x80041013,Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand

Ugly isn’t it? And quite unexpected…

I loved the way he solved the problem by writing functions in PowerShell that leverage the built-in powercfg.exe command. The code is available on the script center gallery on this page.

I started to look around for ways to troubleshoot WMI provider load failures and I found 3 ways:

  • using the official recommendation

It’s at the bottom on this page
So, I run

winmgmt /verifyrepository

…and it wasn’t a surprise that the WMI repository appears to be consistent. Why would it be corrupted? I’ve just loaded a test VM in Azure and removed the GUI from the server.

  • using eventlogs

It’s documented on the heyscriptingguy blog on this page

The problem is that you don’t have the wbemtest.exe and eventvwr.exe utilities on a Core edition but you have PowerShell πŸ˜€

# 1.Enable the WMI Activity log
$log = Get-WinEvent -ListLog * | Where LogName -match "WMI"
$log.set_isEnabled($true)
$log.SaveChanges()

# 2. Read events from the log
Get-WinEvent $log.LogName |
Sort TimeCreated | 
Select -last 4 | 
ft -Wrap -Autosize


The WMI provider is actually loaded from the %systemroot%\system32\PowerWmiProvider.dll file but ends with a new error 0x8007007E.
While this error isn’t on the WMI Error Constants page, we still have PowerShell to help us :-D.
I’ve been using the tip that Windows PowerShell MVP Boe Prox pointed out in the comments on deciphering error codes

[ComponentModel.Win32Exception]0x8007007E

  • using WMI events

I’ve also tested this method that I found on this page and used the tips Windows PowerShell MVP Trevor Sullivan gave on his blog.

$myevent = @()
Register-WmiEvent -Query "Select * From MSFT_WmiSelfEvent" -Action { 
	$global:myevent += $Event 
}
gcim -Namespace root/cimv2/power -Class Win32_Powerplan
$myevent
Get-EventSubscriber | Unregister-Event
$myevent
($myevent | Sort timeGenerated).sourceEventArgs.NewEvent | 
Where Query -ne "Select * From MSFT_WmiSelfEvent" | 
Where __SUPERCLASS -ne "MSFT_WMIThreadPoolEvent"


The resultcode is 2147942526 which is actually the same error as 0x8007007E

Well, there’s another page for super advanced WMI tracing… but when in doubt, I chose to run procmon.

The Get-CIMInstance worked with the minimal server interface. When I removed the Server-Gui-Mgmt-Infra feature I had the WMI provider load failure.

With procmon, I was looking at what files the WMI process was missing.

I copied successively the following missing files back to C:\windows\system32\

C:\Windows\WinSxS\amd64_microsoft-windows-wcl-core_31bf3856ad364e35_6.3.9600.16384_none_77753f69cab9ebdc\wcl.dll
C:\Windows\WinSxS\amd64_microsoft-windows-redhawk-v1.0_31bf3856ad364e35_6.3.9600.16384_none_88cd4c6a90e6d250\slr100.dll
C:\Windows\WinSxS\amd64_microsoft-windows-wcl_31bf3856ad364e35_6.3.9600.16384_none_9bf629752d93ed24\wclPowrProf.dll

And… it started to work again 😎 Long story, quick fix (not always $true, btw):

Advertisements

Tilde issue

I wanted to use the shortcut “~” (a.k.a tilde) in a PowerShell console but it didn’t work.

dir "~/"

I encountered actually the following error:
Get-ChildItem : Cannot retrieve the dynamic parameters for the cmdlet. Home location for this provider is not set. To set the home location, call “(get-psprovider ‘FileSystem’).Home = ‘path'”.

I remembered Windows PowerShell MVP Jim Christopher writing about the difference between ~ and $home.

I also experienced the same gotcha in a runas account.

I wanted to know what was really going on and decided to compare a trace on a failing computer and on a safe one by executing

Trace-Command -Name PathResolution -Expression { 
    Resolve-Path ~ 
} -PSHost


The interesting lines are after “Resolving HOME relative path.”:

Getting home path for provider: FileSystem (on the working one)
ERROR: HOME path not set for provider: FileSystem (on the failing one)

What is really “~”?
The answer is located in the example 3 of the Convert-Path cmdlet and the example 4 of Get-PSProvider cmdlet.

Get-help '~'
Get-help Convert-Path -Examples
Get-help Get-PSProvider -Examples


May I propose my definition πŸ˜›
~ is a relative path that is resolved from the home property of the current provider.

There’s more…

Get-PSProvider "FileSystem" | Get-Member


The definition returned also indicates that I can set my favorite “MSH Path” on every provider.

Now, there are 2 options:
You can either define custom values for all the default providers:

(Get-PSProvider Alias).Home = "Alias:"
(Get-PSProvider Environment).Home = "Env:"
(Get-PSProvider FileSystem).Home = "$home"
(Get-PSProvider Function).Home = "Function:"
(Get-PSProvider Registry).Home = "HKCU:\"
(Get-PSProvider Variable).Home = "Microsoft.PowerShell.Core\Variable:"
(Get-PSProvider Certificate).Home = "Microsoft.PowerShell.Security\Certificate::CurrentUser\My"
(Get-PSProvider WSMan).Home = "WSMan:"

or
Set a home path for all PSProviders that don’t have one already defined:

# Overwrite my home for the FileSystem provider as
# in my env, $home -ne $env:userprofile
(Get-PSProvider -PSProvider FileSystem).Home = $($env:USERPROFILE)
# Dynamically define a home for all the other providers
Get-PSProvider |
Where { -not($_.Home) -and -not($_.Name -eq 'FileSystem')} | 
ForEach-Object {
    if ($_.Drives) {
        (Get-PSProvider $_.Name).Home = (
        '{0}:' -f ($_.Drives | Select -Last 1).Name
        )
        Write-Verbose "Set home for provider $($_.Name) to $($_.Drives | Select -Last 1)" -Verbose
    } else {
        Write-Warning -Message "Drives not found for provider  $($_.Name)"
    }
}
# View the PSProviders and their home
Get-PSProvider | Select Name,Capabilities,Drives,Home

The list of PSProviders returned by the Get-PSProvider cmdlet may vary because

To force things a little bit and get the nice picture below, I’ve already interacted with the default classic PSproviders and ActiveDirectory module before dynamically defining home path properties of the PSProviders with the above code

Get-PSProvider 'Alias','Environment','FileSystem','Function',
'Registry','Variable','Certificate','WSMan' | Out-Null
if (Get-Module -ListAvailable | Where Name -eq ActiveDirectory) { 
    Get-ADUser $($env:USERNAME) -ErrorAction SilentlyContinue | Out-Null
}

By setting the above home paths, the automatic expansion of “~” feature started to work again 😎

Deploying Windows Defender updates with WSUS

Context:

I’ve been recently alerted by a Nessus report that Windows Defender wasn’t up-to-date. Nessus actually raised the following two alerts about the Malware Protection Engine in various products:

The Nessus database references the issues on these pages:

And both Nessus reports link to http://support.microsoft.com/kb/2510781, which says:

Note Windows Defender may be disabled when Microsoft Security Essentials (MSE) or Forefront Endpoint Protection (FEP) is being installed. This is by design, as MSE and FEP are functional supersets of Windows Defender. The currently active product will receive engine and definition updates accordingly.

In other words, just deploying the latest Definition Update for whatever installed Malware Protection Engine would fix these two Nessus alerts πŸ˜€

Hands-on! Update the WSUS server configuration

Note that the following will run on a WSUS server built-in Windows 2012 R2.

# View what are the currently selected products
(Get-WsusServer).GetSubscription().GetUpdateCategories() | 
Format-Table Title,Id -AutoSize

NB: One cannot just add new products or classifications because there’s no method for this purpose 😦

# Select the products I want and update the WSUS config
$subscription = (Get-WsusServer).GetSubscription()
$products = (Get-WsusServer).GetUpdateCategories() | Where {
    $_.Id -in @(
        'bfe5b177-a086-47a0-b102-097e4fa1f807', # Windows 7
        '6407468e-edc7-4ecd-8c32-521f64cee65e', # Windows 8.1
        '8c3fcc84-7410-4a95-8b89-a166a0190486'  # Windows Defender
    )
}
$coll = New-Object -TypeName Microsoft.UpdateServices.Administration.UpdateCategoryCollection
$products | foreach { $coll.Add($_) }
$subscription.SetUpdateCategories($coll)
$subscription.Save()
# View what are the currently selected classifications
(Get-WsusServer).GetSubscription().GetUpdateCategories() |
Format-Table Title,Id -AutoSize
# Select the classifications I want and update the WSUS config
$subscription = (Get-WsusServer).GetSubscription()
$classifications = (Get-WsusServer).GetUpdateClassifications() | Where {
    $_.Id -in @(
        'e6cf1350-c01b-414d-a61f-263d14d133b4', # Critical Updates
        '0fa1201d-4330-4fa8-8ae9-b877473b6441', # Security Updates
        '68c5b0a3-d1a6-4553-ae49-01d3a7827828', # Service Packs
        'e0789628-ce08-4437-be74-2495b842f43b'  # Definition Updates 
    )
}
$coll = New-Object -TypeName Microsoft.UpdateServices.Administration.UpdateClassificationCollection
$classifications  | foreach { $coll.Add($_) }
$subscription.SetUpdateClassifications($coll)
$subscription.Save()

Deploy the Definition updates

First the WSUS needs to be re-synced from its upstream source (Microsoft Update, in my case)

(Get-WsusServer).GetSubscription().StartSynchronization()

Whatever the speed of your ISP link, go and get a coffee or your favorite beverage.
When you’re back, check the progress of the sync.

(Get-WsusServer).GetSubscription().GetSynchronizationStatus()   

…or if it finished, check it’s status

(Get-WsusServer).GetSubscription().GetLastSynchronizationInfo()

There are two methods to deploy definition updates either by using an ADR (Automatic Deployment Rule) or manually. The ADR method is documented on this page KB919772

But let’s do it manually as I don’t like ADR πŸ˜›

Let’s figure out what needs to be deployed:

(Get-WsusServer).SearchUpdates("Defender") | 
Where { 
    $_.PublicationState -ne 'Expired' -and
    -not($_.isSuperseded) } | 
Sort-Object CreationDate | 
Select -Last 2 | 
Format-Table Title,is* -AutoSize

Humm.. there are two different KB. KB915597 is for Windows 7/2008R2 and KB2267602 (that leads to a Oops!! page currently) for Windows 8.1/2012R2

# Select my target group: Windows 7 computers
$targetgroup = (Get-WsusServer).GetComputerTargetGroups() | 
Where Name -eq "Windows 7 x64"

# Make sure nothing is approved
# or mark as 'notapproved' any previously approved Defender updates
# from last month
(Get-WsusServer).SearchUpdates("Defender") | 
Where isApproved | ForEach-Object -Process {            
  $_.Approve(            
[Microsoft.UpdateServices.Administration.UpdateApprovalAction]::NotApproved,            
   $targetgroup            
  )            
 }

# Approve the latest definition update for Windows 7
(Get-WsusServer).SearchUpdates("Defender") | 
Where { 
    $_.PublicationState -ne 'Expired' -and  
    -not($_.isSuperseded)  -and
    ($_.Title -match "915597")
} |
Sort-Object CreationDate | 
Select -Last 1 | foreach {
 $_.Approve(            
[Microsoft.UpdateServices.Administration.UpdateApprovalAction]::Install,            
  $targetgroup            
 )
}

PowerShell and WSUS rocks! No doubt 😎

Troubleshooting the client’s current Active Directory site

I’ve seen a nice tweet a few weeks ago about how to get the client’s current Active Directory Site.

I cannot remember who tweeted it. Kudos to him/her, it’s very handy.

[DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite(
).Name

Of course, there are other (ugly) ways to get it, using gpresult.exe

gpresult /R /scope computer| 
Select-String -Pattern "Site\sName" |
Out-String | 
ConvertFrom-Csv -Delimiter ":" -Header "Property","Value"

… or using nltest.exe

((nltest /DSGETSITE) -split "`n")[0]

The .Net way is now definetly my favorite πŸ˜€

I wondered how far this System.DirectoryServices.ActiveDirectory.ActiveDirectorySite .Net class is available?

I could have find the answer using the following MSDN page:

…but my first attempt was the hard way :\
I remembered that I could easily get the Dll a cmdlet is loaded from with the Get-Command cmdlet:

Get-Command -Name Get-Command | fl Dll


How can I get the same info for a .Net class?

 [System.Reflection.Assembly]::GetAssembly(
 [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]
 ) | fl Location

So, the answer is that the System.DirectoryServices.ActiveDirectory.ActiveDirectorySite .Net class is available as of .Net version 2.0 on any (domain joined) client. Cool, isn’t it? 😎