39

I'm writing a monitoring service that uses WMI to get information from remote machines. Having local admin rights on all these machines is not possible for political reasons.

Is this possible? What permissions/rights does my user require for this?

9 Answers 9

36

The following works on Window 2003 R2 SP 2, Windows Server 2012 R2

  1. Add the user(s) in question to the Performance Monitor Users group
  2. Under Services and Applications, bring up the properties dialog of WMI Control (or run wmimgmt.msc). In the Security tab, highlight Root/CIMV2, click Security; add Performance Monitor Users and enable the options : Enable Account and Remote Enable
  3. Run dcomcnfg. At Component Services > Computers > My Computer, in the COM security tab of the Properties dialog click "Edit Limits" for both Access Permissions and Launch and Activation Permissions. Add Performance Monitor Users and allow remote access, remote launch, and remote activation.
  4. Select Windows Management Instrumentation under Component Services > Computers > My Computer > DCOM Config and give Remote Launch and Remote Activation privileges to Performance Monitor Users Group.

Notes:

  • As an alternatively to step 3 and 4, one can assign the user to the group Distributed COM Users (Tested on Windows Server 2012 R2)
  • If the user needs access to all the namespaces, you can set the settings in 2. at the Root level, and recurse the permissions to the sub-namespaces via the Advanced window in Security
5
  • 4
    I've found steps 2 and 3 aren't necessary if you add the user to Distributed COM Users.
    – Nexus
    Feb 12, 2010 at 0:53
  • Working on WinXP and Win7, I was not able to add the "Distributed COM Users" group - maybe this group is only accessible on servers? It would not resolve during name lookup when trying to add to the permissions. Also, I found that I had to set permissions for WMI on "Root" as well as "Root/CIMV2" and that I had to go into advanced permissions and apply the permission for the subnamespaces as well as the namespace. Aug 3, 2012 at 20:26
  • Works also for Windows 8! Also, does somebody know how to do the same from powershell or some other shell?
    – Bunyk
    Mar 24, 2014 at 9:02
  • 2
    In the case that you want a user to be able to access all the namespace, you can grant the permission to Root and all the sub-namespaces by selecting Root, opening Security then Advanced and setting up the recursion. By default these settings apply only to the selected object and do not cascade.
    – Thomas BDX
    Aug 22, 2014 at 20:23
  • that doesn't work for MSCluster namespace
    – John
    Jan 13, 2016 at 8:52
5

All I did on Windows 8 was added user to group "Remote Management Users", and remote WQL requests worked.

2

By default, only the local Administrators group has remote permissions to WMI. You will have to customise the WMI "Remote Enable" permissions.

2

You may also have to grant "DCOM remote access permissions" and/or "DCOM remote launch and activation permissions" depending on what exactly you are trying to do. This MSDN article gives the step-by-step procedures.

2

Nothing here worked for me. I found a YT vid in conjunction with an arbitrary comment that solved it for me (& hopefully for you, too), but only when using invoke-command (ex. invoke-command -ComputerName MyRemoteComputer -ScriptBlock {Get-Process -Name explorer}).

On the PC you want to remotely access (on same domain):

  1. [Right-click Start] > Run > lusrmgr.msc (on Domain Controllers: dsa.msc)
  2. [Select the Groups folder] (on Domain Controllers: Builtin folder)
  3. [Right-click Distributed COM Users] > Add to [a] group... > Add...
  4. [Search the user/group you want to grant WMI access to] > Check Names (to confirm it's the right group/user) > OK

Why does adding the user/group to the Distributed COM Users group help? Well, that group has privileges to launch applications & objects remotely under COM Security settings. You can view these privileges with: [Right-click Start] > Run > DCOMcnfg > Component Services > Computers > [Right-click My Computer] > Properties > COM Security > Edit Limits...

  1. In an elevated PowerShell: Set-PSSessionConfiguration -ShowSecurityDescriptorUI -Name Microsoft.PowerShell
  2. Add... > [Search the user/group you want to grant WMI access to] > Check Names (to confirm it's the right group/user) > OK
  3. Under the Allow column under Permissions for MyUser, I only selected the Read(Get,Enumerate,Subscribe) and Execute(Invoke) options (since I was granting these privileges to all Domain Users).
  4. Apply > OK
  5. [Right-click Start] > Run > services.msc
  6. [Right-click] > Restart both Windows Management Instrumentation and Windows Remote Management (WS-Management).
  • (Set each of these services' Startup types to Automatic--if not already--in [Right-click] > Properties.)
  1. (If still doesn't work) Open Windows Defender Firewall with Advanced Security and [Right-click] > Enable each Windows Management Instrumentation Inbound Rule.
1

The following worked for me in a 2012 r2 domain environment although I only managed to do it per server and not the entire domain:

1) Add user to Performance Log Users Group. 2) Run wmimgmt.msc, right click "WMI Control (LOCAL), goto Security tab and grant the appropriate user "Enable Account" and "Remote Enable" on the desired namespace (usualy CIMV2).

If I manage to get it done for the entire domain I'll come back and update.

1

Based on the chosen answer, I modified the script from Microsoft to set the WMI security. My test user was a non-administrative domain user who was a member of the "Remote Management Users" on the local system for reasons not related to this issue. After granting my user the EnableAccount, RemoteEnable, and ExecuteMethods permissions on the target namespace, I was able to access WMI.

So, I did not add my user to the Performance Monitor Users or Distributed COM Users local groups.

A couple of notes regarding the script:

  1. You must specify the full path of the namespace. In my case, the namespace was Root/Microsoft/SqlServer
  2. Inheritance was wrong. Because there are no leaf objects you can't use $OBJECT_INHERIT_ACE_FLAG
  3. I got rid of the embedded function because it was too small and it was only used once.

The script is below. I named it Set-WMINamespaceSsecurity.ps1

Param ([Parameter(Mandatory=$true,Position=0)] [string]$Namespace,
       [Parameter(Mandatory=$true,Position=1)] [ValidateSet("Add","Remove")] [string]$Operation,
       [Parameter(Mandatory=$true,Position=2)] [string] $Account,
       [Parameter(Mandatory=$false,Position=3)] [ValidateSet("EnableAccount","ExecuteMethods","FullWrite","PartialWrite","ProviderWrite","RemoteEnable","ReadSecurity","WriteSecurity")] [string[]] $Permissions=$null,
       [Parameter(Mandatory=$false)] [switch]$AllowInherit,
       [Parameter(Mandatory=$false)] [switch]$Deny,
       [Parameter(Mandatory=$false)] [string]$ComputerName=".",
       [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential]$Credential=$null)

$OBJECT_INHERIT_ACE_FLAG    = 0x1
$CONTAINER_INHERIT_ACE_FLAG = 0x2
$ACCESS_ALLOWED_ACE_TYPE    = 0x0
$ACCESS_DENIED_ACE_TYPE     = 0x1

$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_S_SUBJECT_TO_SDS  = 0x43003

$ErrorActionPreference = "Stop"

$InvokeParams=@{Namespace=$Namespace;Path="__systemsecurity=@";ComputerName=$ComputerName}
if ($PSBoundParameters.ContainsKey("Credential")) { $InvokeParams+= @{Credential=$Credential}}

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

$ACL = $output.Descriptor

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

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

# Add Operation
if ($Operation -eq "Add") {
  if ($Permissions -eq $null) { throw "Permissions must be specified for an add operation" }

  # Construct AccessMask
  $AccessMask=0
  $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="EnableAccount","ExecuteMethods","FullWrite","PartialWrite","ProviderWrite","RemoteEnable","ReadSecurity","WriteSecurity"
  $PermissionTable=@{}
  for ($i=0; $i -lt $WBEM_RIGHTS_FLAGS.Count; $i++) { $PermissionTable.Add($WBEM_RIGHTS_STRINGS[$i].ToLower(), $WBEM_RIGHTS_FLAGS[$i]) }
  foreach ($Permission in $Permissions) { $AccessMask+=$PermissionTable[$Permission.ToLower()] }

  $ACE=(New-Object System.Management.ManagementClass("Win32_Ace")).CreateInstance()
  $ACE.AccessMask=$AccessMask
  # Do not use $OBJECT_INHERIT_ACE_FLAG.  There are no leaf objects here.
  if ($AllowInherit.IsPresent) { $ACE.AceFlags=$CONTAINER_INHERIT_ACE_FLAG }
  else { $ACE.AceFlags=0 }

  $Trustee=(New-Object System.Management.ManagementClass("Win32_Trustee")).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
}
#Remove Operation
else {
  if ($Permissions -ne $null) { Write-Warning "Permissions are ignored for a remove operation" }
  [System.Management.ManagementBaseObject[]]$newDACL = @()
  foreach ($ACE in $ACL.DACL) {
    if ($ACE.Trustee.SidString -ne $Win32Account.SID) { $newDACL+=$ACE }
  }
  $ACL.DACL = $newDACL
}

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

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

If you are trying to access WMI there are two protocols available: DCOM and WinRM. Which protocol you are using will depend on the tools or API being used. For example, when you are using PowerShell, the 'older' Get-WmiObject cmdlet uses DCOM, whereas the 'newer' Get-CimInstance cmdlet uses WinRM.

To access WMI as a non-administrator using DCOM you need the following groups / permissions

  1. Distributed COM Users
  2. 'Remote Enable' permissions under WMI control for the namespace you want to access (you can either assign these manually, or with a script/Group Policy)
  3. Performance Monitor Users (for accessing certain performance metrics via WMI e.g. CPU usage via the Win32_Processor LoadPercentage value)

To access WMI as a non-administrator using WinRM you need the following groups / permissions

  1. Remote Management Users OR WinRMRemoteWMIUsers_ (notwithstanding the Microsoft official documentation they seem to function the same - and in later versions of Windows only the first is available)
  2. 'Remote Enable' permissions under WMI control for the namespace you want to access (you can either assign these manually, or with a script/Group Policy)
  3. Performance Monitor Users (for accessing certain performance metrics via WMI e.g. CPU usage via the Win32_Processor LoadPercentage value)

Also to do any of this remotely, make sure you have the 'Windows Management Instrumentation' rules enabled in Windows Defender Firewall for the relevant scope.

(You can also use the 'Performance Log Users' group to assign DCOM and WMI performance metric permissions instead of 'Distributed COM Users' and 'Performance Monitor Users', but this will grant additional privileges that may not be appropriate from a security perspective. As suggested in other answers, you can also set up the DCOM Remote Launch and Activation Permissions manually, but that seems overly complicated since both 'Distributed COM Users' and 'Performance Log Users' are already assigned those permissions by default.)

1
  • The gist script you shared saved my day. Almost applied all the steps suggested in this and other answers but I was getting "-2147217405 = WBEM_E_ACCESS_DENIED - Access Denied: Current user does not have permission to perform the action." through winrm+non admin+VBScript(cscript). I may have missed one the manual steps.
    – caltuntas
    Aug 14, 2023 at 12:03
0

We did this for PRTG: We created a new domain user: Created a GPO Dit to put his user in the group "Performnce Log Users" and used a powershell script to add this user to WMI Control. thanks to:

https://live.paloaltonetworks.com/t5/Management-Articles/PowerShell-Script-for-setting-WMI-Permissions-for-User-ID/ta-p/53646

1
  • Please include relevant information directly in your answers. Answers posted should be able to stand alone, and just use links for reference. Jun 2, 2017 at 18:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .