Share via


Set WMI Namespace permissions for service account - 2003-2008r2 (2012 if possible).

Question

Friday, November 8, 2013 6:20 PM

Howdy,

I have scoured the net on how to set the permissions for a service account in wmi for root\cimv2 so the account can monitor systems. I found a script that I read was supposed to work on 2003 but it does not and it appears it is because the property is not available on 2003.  The script is below.  Does anyone have one that will work for 2003-2008r2 (and 2012 would be great), or even one for 2003 and then I can use this for 2008?

# Copyright (c) Microsoft Corporation.  All rights reserved. 
# For personal use only.  Provided AS IS and WITH ALL FAULTS.
 
# Set-WmiNamespaceSecurity.ps1
# Example: Set-WmiNamespaceSecurity root/cimv2 add steve Enable,RemoteAccess
Function Set-WmiNamespaceSecurity {
 
Param ( [parameter(Mandatory=$true,Position=0)][string] $namespace,
    [parameter(Mandatory=$true,Position=1)][string] $operation,
    [parameter(Mandatory=$true,Position=2)][string] $account,
    [parameter(Position=3)][string[]] $permissions = $null,
    [bool] $allowInherit = $false,
    [bool] $deny = $false,
    [string] $computer = ".",
    [System.Management.Automation.PSCredential] $credential = $null)
   
Process {
    $ErrorActionPreference = "Stop"
 
    Function Get-AccessMaskFromPermission($permissions) {
        $WBEM_ENABLE            = 1
                $WBEM_METHOD_EXECUTE = 2
                $WBEM_FULL_WRITE_REP   = 4
                $WBEM_PARTIAL_WRITE_REP              = 8
                $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 = "Enable","MethodExecute","FullWrite","PartialWrite",
            "ProviderWrite","RemoteAccess","ReadSecurity","WriteSecurity"
 
        $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 "Unknown permission: $permission</code>nValid permissions: $($permissionTable.Keys)"
            }
            $accessMask += $permissionTable[$permission.ToLower()]
        }
       
        $accessMask
    }
 
    if ($PSBoundParameters.ContainsKey("Credential")) {
        $remoteparams = @{ComputerName=$computer;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
    $OBJECT_INHERIT_ACE_FLAG = 0x1
    $CONTAINER_INHERIT_ACE_FLAG = 0x2
 
    $computerName = (Get-WmiObject @remoteparams Win32_ComputerSystem).Name
   
    if ($account.Contains('\)) {
        $domainaccount = $account.Split('\)
        $domain = $domainaccount[0]
        if (($domain -eq ".") -or ($domain -eq "BUILTIN")) {
            $domain = $computerName
        }
        $accountname = $domainaccount[1]
    } elseif ($account.Contains('@')) {
        $domainaccount = $account.Split('@')
        $domain = $domainaccount[1].Split('.')[0]
        $accountname = $domainaccount[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"
    }
 
    switch ($operation) {
        "add" {
            if ($permissions -eq $null) {
                throw "-Permissions must be specified for an add operation"
            }
            $accessMask = Get-AccessMaskFromPermission($permissions)
   
            $ace = (New-Object System.Management.ManagementClass("win32_Ace")).CreateInstance()
            $ace.AccessMask = $accessMask
            if ($allowInherit) {
                $ace.AceFlags = $OBJECT_INHERIT_ACE_FLAG + $CONTAINER_INHERIT_ACE_FLAG
            } else {
                $ace.AceFlags = 0
            }
                       
            $trustee = (New-Object System.Management.ManagementClass("win32_Trustee")).CreateInstance()
            $trustee.SidString = $win32account.Sid
            $ace.Trustee = $trustee
           
            $ACCESS_ALLOWED_ACE_TYPE = 0x0
            $ACCESS_DENIED_ACE_TYPE = 0x1
 
            if ($deny) {
                $ace.AceType = $ACCESS_DENIED_ACE_TYPE
            } else {
                $ace.AceType = $ACCESS_ALLOWED_ACE_TYPE
            }
 
            $acl.DACL += $ace.psobject.immediateBaseObject
   
        }
       
        "delete" {
            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
        }
       
        default {
            throw "Unknown operation: $operation`nAllowed operations: add delete"
        }
    }
 
    $setparams = @{Name="SetSecurityDescriptor";ArgumentList=$acl.psobject.immediateBaseObject} + $invokeParams
 
    $output = Invoke-WmiMethod @setparams
    if ($output.ReturnValue -ne 0) {
        throw "SetSecurityDescriptor failed: $($output.ReturnValue)"
    }
}
}

All replies (4)

Friday, November 15, 2013 4:36 AM ✅Answered

Hi,

To modify the script as your need, I would like to recommend you to go to the The Official Scripting Guys Forum for more effective support. 

http://social.technet.microsoft.com/Forums/windowsserver/en-US/home?forum=ITCG

In addition, If you can not find a good script to work as you expect, you can also refer to this article to Set WMI Namespace permissions without script:

HOW TO: Set WMI Namespace Security in Windows Server 2003:

http://support.microsoft.com/kb/325353

Thanks for your understanding.

Best Regards,

Anna

We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time.
Thanks for helping make community forums a great place.


Saturday, November 9, 2013 6:03 AM

Hi Domran,

I believe you are referring to the the code snippets in the WMI Namespace Security Blog posts by Steve Lee

http://blogs.msdn.com/b/wmi/archive/tags/wmi+namespace+security/

I went through these blog post + comments and found out that this script will work for Vista/Server 2008 ( and above ) as the Method GetSecurityDescriptor was added to them. But for previous OS we have the methods GetSD and SetSD... and he points out that 

On older systems, these two methods are not available and you would have to use GetSD and SetSD which work on SECURITY_DESCRIPTOR structs represented as binary arrays.  Discussion on how to use those methods (which is only really possible with C/C++) is outside of the scope of this blog post.   

So you are good to use it on OS Server 2008 and above .....for OS prior to these, maybe someone else will point out

Hope this helps

Knowledge is Power{Shell}.


Monday, November 11, 2013 11:34 AM

Hi Domran,

Thanks for your posting.

To get the existing security on the namespace, we can use “GetSD” as the method, which can work on windows server 2003.

For detailed information about using this method to get security in powershell, please refer to this article:

Script remote DCOM / WMI access for a non admin

I hope this helps.

We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time.
Thanks for helping make community forums a great place.


Wednesday, November 13, 2013 4:43 AM

Thanks, I had found that post, and was going to try the script, but I thought it made more sense to add the service account to the DCOM users group using a GPO with the preferences GPO option, and just set the WMI permissions.

I'm not sure what I can take out of this script to only set the WMI permissions and not set dcom permissions, or on 2003 do I have to do both portions in this script?  I tried manually setting the permissions by adding the service account to the performance log and performance monitor users, as well as the dcom users group, then setting permissions for the wmi root\cimv2 and that seems to work, it's an open source monitoring app that is going to use the service account.

The permissions are:

  • Execute methods
  • Enable Account
  • Remote Enable