Grant WMI Remote Enable Right to AD Group

I have to deploy a new monitoring tool that requires WMI Remote Enable rights to the //root/CIMv2 namespace on all monitored systems. The systems I need to monitor run Windows Server 2003 Service Pack 2 or newer and PowerShell 2.0 or newer is installed on all of the servers.

I have found some scripts on the Internet that overwrite existing permissions using a template, but this isn’t a good fit for my environment. We have some services (such as Hyper-V) that add permissions to this namespace that wouldn’t apply to all servers. I’d rather just add to the existing namespace permissions using PowerShell (or any other method that can be scripted) rather than manually changing permissions using the WMI Control console.

Is this even possible?

Thanks in advance for any suggestions.

It’s possible, but the code will be a bit complex. Here are a couple of examples of using the .NET Framework to modify WMI namespace security, which can serve as a guide for doing the same thing in PowerShell:

http://msdn.microsoft.com/en-us/library/ms257349(v=vs.80).aspx

They both use the same basic approach: Obtain a binary representation of the Security Descriptor from the __SystemSecurity.GetSD() method. Use the Win32 API to convert that to a SDDL string. Add a new ACE to the SDDL string. Convert the new SDDL string back to binary with the Win32 API, and finally use __SystemSecurity.SetSD() to write the new descriptor back to the WMI namespace.

If you prefer (and if you’re using Vista / 2008 or later), the __SystemSecurity class also has methods named GetSecurityDescriptor() and SetSecurityDescriptor(). Unlike GetSD() / SetSD(), these methods allow you to work with WMI classes instead of binary / SDDL representations of the security descriptor (__SecurityDescrptor, __ACE, and __Trustee, primarily.) You can find information on these WMI system classes here: http://msdn.microsoft.com/en-us/library/aa394583(v=vs.85).aspx

I released a module to the Script Center Repository that can simplify this. It was designed for PSv3, but I’ve recently updated it to work with PSv2 (please let me know if you find something that doesn’t work).

Here’s how you would do it using the WMI methods that Dave mentioned:

$BinarySD = Get-WmiObject __SystemSecurity | Invoke-WmiMethod -Name GetSD | select -exp SD
$SD = New-AdaptedSecurityDescriptor -BinarySD $BinarySD -AccessMaskEnumeration ([PowerShellAccessControl.WmiNamespaceRights])

# Take a look at the current access:
$SD | Get-AccessControlEntry | ft -Wrap

# Create a new entry for 'Users' (change group to desired name):
$ACE = New-AccessControlEntry -AccessMask ([PowerShellAccessControl.WmiNamespaceRights]::RemoteEnable) -Principal Users -AppliesTo ThisObjectAndChildContainers

# Add the ACE
$SD.AddAccessRule($ACE)

# Take a look at the new access:
$SD | Get-AccessControlEntry | ft -Wrap

$NewBinarySD = $SD.GetSecurityDescriptorBinaryForm()

# Save it (you'll have to remove the -WhatIf; I suggest you test this first)
Get-WmiObject __SystemSecurity | Invoke-WmiMethod -Name SetSD -ArgumentList (,([byte[]] $NewBinarySD)) -WhatIf

There are two lines there that aren’t necessary, but I would run them the first time you go through it interactively (the lines that call Get-AccessControlEntry).

Here’s the alternate method Dave was talking about on a Vista and higher system (NOTE: There is a bug in the Win32_SecurityDescriptorHelper method that I use to convert from Win32SD to SDDL and/or BinarySD that doesn’t preserve inheritance flags. For that reason, just read this snippet for how it will look in the future, and read the next snippet for the current workaround):

# Get-CimInstance would work here, too
$SD = Get-WmiObject __SystemSecurity | Get-Win32SecurityDescriptor -Sddl | New-AdaptedSecurityDescriptor -AccessMaskEnumeration ([PowerShellAccessControl.WmiNamespaceRights])

# Create a new entry for 'Users' (change group to desired name):
$ACE = New-AccessControlEntry -AccessMask ([PowerShellAccessControl.WmiNamespaceRights]::RemoteEnable) -Principal Users -AppliesTo ThisObjectAndChildContainers
$SD.AddAccessRule($ACE)

# NOTE: There is a function called ConvertTo-Win32SecurityDescriptor that will do the following, but I just realized
#       it only returns CimInstance objects. I'll add it to the list of things to fix in a future release. For now,
#       this will work:
$Win32SD = Invoke-WmiMethod -Class Win32_SecurityDescriptorHelper -Name SDDLToWin32SD -ArgumentList $SD.Sddl | select -ExpandProperty Descriptor

# I haven't put the functions I use to save SDs into the module yet, so save manually (remove -WhatIf after testing):
Invoke-WmiMethod -Class __SystemSecurity -Name SetSecurityDescriptor -ArgumentList $Win32SD -WhatIf

And, one more example that doesn’t use Win32_SecurityDescriptorHelper to convert from Win32SD to SDDL. This one is using the CIM classes; just change those calls to the WMI calls in the previous example if you need to use them:

# Only use this if there is no SACL:
$SDDL = Get-CimInstance __SystemSecurity | Get-Win32SecurityDescriptor -SddlNew | select -exp SddlNew
$SD = New-AdaptedSecurityDescriptor -Sddl $SDDL -AccessMaskEnumeration ([PowerShellAccessControl.WmiNamespaceRights])

# Create a new entry for 'Users' (change group to desired name):
$ACE = New-AccessControlEntry -AccessMask ([PowerShellAccessControl.WmiNamespaceRights]::RemoteEnable) -Principal Users -AppliesTo ThisObjectAndChildContainers
$SD.AddAccessRule($ACE)

# I haven't put the functions I use to save SDs into the module yet, so save manually (remove -WhatIf after testing):
$Win32SD = $SD | ConvertTo-Win32SecurityDescriptor -ValueOnly
Invoke-CimMethod -ClassName __SystemSecurity -MethodName SetSecurityDescriptor -Arguments @{ Descriptor = $Win32SD } -WhatIf

Please try it out and let me know if it works for you :slight_smile:

One more thing: I kept using [PowerShellAccessControl.WmiNamespaceRights] above. That’s just an enumeration that makes it so you don’t have to work with the raw numbers for access control, but there is absolutely nothing wrong with just providing the numeric value to the -AccessMask for New-AccessControlEntry if you know it. You could also leave the -AccessMaskEnumeration parameter off to the New-AdaptedSecurityDescriptor call (when viewing the SD, it would show the numeric mask instead of the friendly text).

Let me know if that doesn’t make sense, and I’ll provide some examples.

I’ve released an update to the module that simplifies the usage:

# Change the namespace to whatever you'd like
$SD = Get-WmiObject __SystemSecurity | Get-SecurityDescriptor

# Create a new entry for 'Users' (change group to desired name) (AppliesTo could also be 'ThisObjectAndChildContainers'):
$ACE = New-AccessControlEntry -WmiNamespaceRights RemoteEnable -Principal Users -AppliesTo Object, ChildContainers
$SD.AddAccessRule($ACE)

$Win32SD = $SD | ConvertTo-Win32SecurityDescriptor -LegacyWmiObject -ValueOnly

# Set-SecurityDescriptor isn't quite ready for prime time, so you're still stuck
# doing this:
Invoke-WmiMethod -Class __SystemSecurity -Name SetSecurityDescriptor -ArgumentList $Win32SD -WhatIf

It also does some custom formatting, so these commands would look pretty nice (after defining $SD above):

# Show the object itself:
$SD

# Show it in a list format:
$SD | fl

# Show the access entries:
$SD.Access

# Show access and audit entries (if there are any)
$SD | Get-AccessControlEntry

# This will look cooler than the WMI namespace:
Get-Service b* | Get-AccessControlEntry

And I think the latest release finally has a complete solution. You can do it using the Get-SecurityDescriptor; <modify>; Set-SecurityDescriptor model, but here’s a simplified one-liner that should work:

Get-WmiObject __SystemSecurity | Add-AccessControlEntry -Principal Users -WmiNamespaceRights RemoteEnable -WhatIf

If it looks like it will work, remove the -WhatIf parameter. If you don’t want the prompt when it tries to save the security descriptor, use the -Force parameter.

And if you have any issues or suggestions, please use the Q&A section on that site.