6

From various documentation it appears that to change WMI access you need to use WMI to access the running service and modify specific parts of the tree.

Its kind of annoying changing 150,000 hosts using the UI.
And then having to include such changes in the process of adding new hosts.

Could write a script to do the same, but that needs to either connect to all those machines live, or be distributed for later update say in an startup/install script. And then you have to mess around with copying binary SD data from an example access control.

I've also found you can change the wbem/*.mof file to include an SDDL but I'm really vague on how that all works at the moment.

Am I just missing some point of simple administration?

4 Answers4

6

Made some research on this and looks like the method below should work:

For Windows 2003 with Group Policy Management Console (GPMC), perform the following steps:

  1. Navigate to Start Menu > Administrative Tools > Group Policy Management.
  2. In the left-hand pane, navigate to Forest: Domain Name -> Domains -> Domain Name, where Domain Name is the name of the domain you wish to modify.
  3. Right-click on Domain Name in the left-hand pane and select Create and Link a GPO Here.
  4. Name the new policy WMI Permissions.

NOTE: Since WMI must establish a DCOM connection to remote host, this is enough to configure access permissions for DCOM.

Configuring Distributed Component Object Model (DCOM) Permissions:

  1. Navigate to the WMI Permissions group policy, either by the Group Policy Management plug-in or by the ADUC plug-in.
  2. Ensure that the WMI Permissions policy is highlighted and click on the Edit button.
  3. Navigate to Computer Configuration -> Policies -> Windows Settings -> Security Settings -> Local Policies -> Security Options.
  4. In the right-hand UI pane, double-click on DCOM: Machine Access Restrictions in Security Descriptor Definition Language (SDDL) syntax.
  5. Put a checkmark in the box beside Define this policy setting.
  6. Click on the Edit Security button.
  7. Click on the Add button; in the resulting pop-up window, specify the domain administrator account that will be used.
  8. Click OK.
  9. In the Group or user names field, select the domain administrator you specified in step #7.
  10. In the Permissions for Administrators field, ensure that there is a checkmark in the Allow column for the Remote Access option.
  11. Click OK.
  12. Click OK.
  13. In the right-hand UI pane, double-click on DCOM: Machine Launch Restrictions in Security Descriptor Definition Language (SDDL) syntax.
  14. Put a checkmark in the box beside Define this policy setting.
  15. Click on the Edit Security button.
  16. Click on the Add button; in the resulting pop-up window, specify the domain administrator account that will be used.
  17. Click OK.
  18. In the Group or user names field, select the domain administrator you specified in step #16.
  19. In the Permissions for Administrators field, ensure that there is a checkmark under the Allow column for both Remote Launch and Remote Activation.
  20. Click OK.
  21. Click OK.
  22. Close the Group Policy Object Editor window.
  23. Click OK and close the Active Directory Users and Computers window.
2

Referencing https://answers.splunk.com/answers/2703/how-to-enable-wmi-data-collection-on-a-domain-server.html

There's no non-esoteric way at this time to globally configure WMI security settings domain-wide. Each machine has its own setting. There is an MSDN blog, however, that lists the steps you can take to create a script that contains the appropriate security descriptors, which you can then subsequently throw into a GPO as a startup script and have your computers get the updated security settings at boot time.

Here's the link to the blog post w/ method for the creating a script: https://blogs.msdn.microsoft.com/spatdsg/2007/11/21/set-wmi-namespace-security-via-gpo-script/

This approach gave me a usable method to enable WMI access for a non-Domain Admin service account using GPO.

1

Microsoft have another, more recent article demonstrating how to do this via Group Policy with a PowerShell script:

https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/delegate-wmi-access-to-domain-controllers/ba-p/259535

Not sure why they do it with a scheduled task. I believe Group Policy Startup Scripts now support PowerShell natively.

The original script is still available but I suggest using this copy which fixes a bug.

Minkus
  • 380
0

The answers that say, "You have to do it from a script" are pretty much right. There is not currently (as of 2025) a native way to do it through Group Policy. Here are two scripts that may help:

Get-WMINameSpaceSecurity:

<#
.Synopsis
   A Script for getting the current security descriptor of a WMI namespace. 
.DESCRIPTION
   A Script for getting the current security descriptor of a WMI namespace. 
.EXAMPLE
   Get-WMINamespaceSecurity.ps1 -NameSpace "root\cimv2"
.EXAMPLE
   Get-WmiNamespaceSecurity.ps1 "root\cimv2" -ComputerName <remote computer>
.NOTES
   Blog links:

https://blogs.msdn.microsoft.com/wmi/2009/07/20/scripting-wmi-namespace-security-part-1-of-3/ https://blogs.msdn.microsoft.com/wmi/2009/07/23/scripting-wmi-namespace-security-part-2-of-3/ https://blogs.msdn.microsoft.com/wmi/2009/07/27/scripting-wmi-namespace-security-part-3-of-3/

Original Content by Jason Eberhardt #> Param ([Parameter (Mandatory=$true,Position=0)] [string]$Namespace, [Parameter(Mandatory=$false)] [string]$ComputerName=".", [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential=$null)

Begin { #https://learn.microsoft.com/en-us/windows/win32/api/wbemcli/ne-wbemcli-wbem_security_flags #Spaces around the "=" sign are required for syntax [Flags()] enum WBEMSecurity { Enable = 0x01 MethodExecute = 0x02 FullWrite = 0x04 PartialWrite = 0x08 ProviderWrite = 0x10 RemoteAccess = 0x20 Subscribe = 0x40 Publish = 0x80 ReadSecurity = 0x20000 WriteSecurity = 0x40000 } #https://learn.microsoft.com/en-us/previous-versions/windows/desktop/secrcw32prov/win32-ace [Flags()] enum WBEMInheritance { OBJECT_INHERIT = 0x01 CONTAINER_INHERIT = 0x02 NO_PROPAGATE_INHERIT = 0x04 INHERIT_ONLY = 0x08 INHERITED = 0x10 SUCCESSFUL_ACCESS_FLAG = 0x40 FAILED_ACCESS_FLAG = 0x80 } }

Process { if ($PSBoundParameters.ContainsKey("Credential")) { $Output=Invoke-WmiMethod -Namespace $Namespace -Path "__systemsecurity=@" -ComputerName $ComputerName -Name "GetSecurityDescriptor" } else { $Output=Invoke-WmiMethod -Namespace $Namespace -Path "__systemsecurity=@" -ComputerName $ComputerName -Credential $Credential -Name "GetSecurityDescriptor" }

if ($Output.ReturnValue -ne 0) {throw "GetSecurityDescriptor failed: $($Output.ReturnValue)" }

$ACL=$Output.Descriptor.DACL $Results=@() for ($i=0; $i -lt $ACL.Count; $i++) { $Results+=[PSCustomObject]@{Trustee=$ACL[$i].Trustee.Domain+"&quot;+$ACL[$i].Trustee.Name;AccessType=[System.Security.AccessControl.AccessControlType]$ACL[$i].AceType;AccessFlags=[WBEMInheritance]$ACL[$i].AceFlags;AccessMask=[WBEMSecurity]$ACL[$i].AccessMask} }

$Results }

Set-WMINameSpaceSecurity:

<#
.Synopsis
   A Script for modifying the current security descriptor of a WMI namespace. 
.DESCRIPTION
   A Script for modifying the current security descriptor of a WMI namespace. 
.EXAMPLE
   Set-WMINamespaceSecurity.ps1 -namespace root/cimv2 -Account "contoso\AD - Remote WMI Access" -operation Add -permissions Enable
.EXAMPLE
   Set-WmiNamespaceSecurity.ps1 root/cimv2 add steve Enable,RemoteAccess
.NOTES
   Blog links:

https://blogs.msdn.microsoft.com/wmi/2009/07/20/scripting-wmi-namespace-security-part-1-of-3/ https://blogs.msdn.microsoft.com/wmi/2009/07/23/scripting-wmi-namespace-security-part-2-of-3/ https://blogs.msdn.microsoft.com/wmi/2009/07/27/scripting-wmi-namespace-security-part-3-of-3/

Original Content by Steve Lee Modified by Graeme Bray Modified a lot by Jason Eberhardt #> Param ([Parameter (Mandatory=$true,ParameterSetName="ADD")] [Parameter (Mandatory=$true,ParameterSetName="DEL")] [string]$Namespace, [Parameter (Mandatory=$true,ParameterSetName="ADD")] [switch]$Add, [Parameter (Mandatory=$true,ParameterSetName="DEL")] [switch]$Delete, [Parameter (Mandatory=$true,ParameterSetName="ADD")] [Parameter (Mandatory=$true,ParameterSetName="DEL")] [string]$Account, [Parameter (Mandatory=$true,ParameterSetName="ADD")] [ValidateSet("PartialWrite","Enable","ProviderWrite","ReadSecurity","WritesSecurity","MethodExecute","RemoteAccess","FullWrite")] [string[]]$Permissions, [Parameter (Mandatory=$false,ParameterSetName="ADD")] [switch]$AllowInherit, [Parameter (Mandatory=$false,ParameterSetName="ADD")] [switch]$Deny, [Parameter (Mandatory=$false,ParameterSetName="ADD")] [Parameter (Mandatory=$false,ParameterSetName="DEL")] [string]$ComputerName=".", [Parameter (Mandatory=$false,ParameterSetName="ADD")] [Parameter (Mandatory=$false,ParameterSetName="DEL")] [System.Management.Automation.PSCredential]$Credential=$null)

Begin { $ACCESS_ALLOWED_ACE_TYPE=0x0 $ACCESS_DENIED_ACE_TYPE=0x1 $OBJECT_INHERIT_ACE_FLAG=0x1 $CONTAINER_INHERIT_ACE_FLAG=0x2 }

Process { Function Get-AccessMaskFromPermission($Permissions) { $WBEM_ENABLE = 0x01 $WBEM_METHOD_EXECUTE = 0x02 $WBEM_FULL_WRITE_REP = 0x04 $WBEM_PARTIAL_WRITE_REP = 0x08 $WBEM_WRITE_PROVIDER = 0x10 $WBEM_REMOTE_ACCESS = 0x20 $WBEM_RIGHT_SUBSCRIBE = 0x40 $WBEM_RIGHT_PUBLISH = 0x80 $READ_CONTROL = 0x20000 $WRITE_DAC = 0x40000

$WBEM_RIGHTS_FLAGS=$WBEM_ENABLE,$WBEM_METHOD_EXECUTE,$WBEM_FULL_WRITE_REP,$WBEM_PARTIAL_WRITE_REP,$WBEM_WRITE_PROVIDER,$WBEM_REMOTE_ACCESS,$READ_CONTROL,$WRITE_DAC
$WBEM_RIGHTS_STRINGS=&quot;Enable&quot;,&quot;MethodExecute&quot;,&quot;FullWrite&quot;,&quot;PartialWrite&quot;,&quot;ProviderWrite&quot;,&quot;RemoteAccess&quot;,&quot;ReadSecurity&quot;,&quot;WriteSecurity&quot;
$PermissionTable=@{}

for ($i=0; $i -lt $WBEM_RIGHTS_FLAGS.Length; $i++) {
  $PermissionTable.Add($WBEM_RIGHTS_STRINGS[$i].ToLower(), $WBEM_RIGHTS_FLAGS[$i])
}

$AccessMask=0

foreach ($Permission in $Permissions) {
  if (-not $PermissionTable.ContainsKey($Permission.ToLower())) {
    throw &quot;Unknown permission: $Permission`nValid permissions: $($PermissionTable.Keys)&quot;
  }
  $AccessMask+=$PermissionTable[$Permission.ToLower()]
}

$AccessMask

} #Get-AccessMaskFromPermission

if ($PSBoundParameters.ContainsKey("Credential")) { $RemoteParams=@{ComputerName=$ComputerName;Credential=$Credential} } else {$RemoteParams=@{ComputerName=$ComputerName}}

$InvokeParams=@{Namespace=$Namespace;Path="__systemsecurity=@"}+$RemoteParams

$Output=Invoke-WmiMethod @InvokeParams -Name "GetSecurityDescriptor" if ($Output.ReturnValue -ne 0) {throw "GetSecurityDescriptor failed: $($Output.ReturnValue)" }

$ACL=$Output.Descriptor

if ($ComputerName -eq ".") { $ComputerName=$env:COMPUTERNAME }

Local or NT-style accounts

if ($Account.Contains('')) { $DomainAccount=$Account.Split('') $Domain=$DomainAccount[0] if (($Domain -eq ".") -or ($Domain -eq "BUILTIN")) { $Domain=$ComputerName } $AccountName=$DomainAccount[1] }

Modern style accounts

elseif ($Account.Contains('@')) { $DomainAccount=$Account.Split('@') $Domain=(Get-ADDomain -Server $DomainAccount[1]).NetBIOSName $AccountName=$DomainAccount[0] }

Local accounts only

else { $Domain=$ComputerName $AccountName=$Account }

$SetParams=@{Class="Win32_Account";Filter="Domain='$Domain' and Name='$AccountName'"}+$RemoteParams $Win32Account=Get-WmiObject @SetParams if ($Win32Account -eq $null) { throw "Account was not found: $Account" }

if ($Add.IsPresent) { if ($Permissions -eq $null) { throw "-Permissions must be specified for an add operation" } $AccessMask=Get-AccessMaskFromPermission($Permissions)

$ACE=(New-Object System.Management.ManagementClass(&quot;win32_Ace&quot;)).CreateInstance()
$ACE.AccessMask=$AccessMask
if ($AllowInherit.IsPresent) { $ACE.AceFlags=$CONTAINER_INHERIT_ACE_FLAG }
else { $ACE.AceFlags=0 }

$Trustee=(New-Object System.Management.ManagementClass(&quot;win32_Trustee&quot;)).CreateInstance()
$Trustee.SidString=$Win32Account.Sid
$ACE.Trustee=$Trustee

if ($Deny.IsPresent) { $ACE.AceType=$ACCESS_DENIED_ACE_TYPE }
else { $ACE.AceType=$ACCESS_ALLOWED_ACE_TYPE }

$ACL.DACL += $ace.psobject.immediateBaseObject

} else { if ($Permissions -ne $null) { throw "Permissions cannot be specified for a delete operation" }

[System.Management.ManagementBaseObject[]]$newDACL=@()
foreach ($ACE in $ACL.DACL) {
  if ($ACE.Trustee.SIDString -ne $Win32Account.SID) { $NewDACL+=$ACE.PSObject.ImmediateBaseObject }
}

$ACL.DACL=$NewDACL.PSObject.ImmediateBaseObject

}

$SetParams=@{Name="SetSecurityDescriptor";ArgumentList=$ACL.PSObject.ImmediateBaseObject}+$InvokeParams

$Output=Invoke-WmiMethod @SetParams if ($output.ReturnValue -ne 0) { throw "SetSecurityDescriptor failed: $($Output.ReturnValue)" } }