Recently I was asked to create an inventory process that would query active directory for servers and gather data on their hardware.
I said “No problem”, fired up my trusty domain admin account and ran a bunch of WMI calls against the servers. Of course, for a script that is to be run on a daily basis, a domain admin is NOT the account of choice.
So, I needed to create a domain user account that could access WMI on all the servers.
Additionally, on a certain percentage of remote servers, my domain admin account was getting an error that I traced to DCOM not allowing access. When I cleared that up, using dcomcnfg, I realized the domain user would also need remote DCOM privileges.
So, my problem was two-fold:
1) Allow remote WMI for a domain user.
2) Allow remote DCOM for a domain user.
I thought I was going to have to modify the firewalls on my servers, but that was already done when they were built.
We have 557 Windows servers in the department, so I wasn’t about to do this by hand.
Of those 557 servers, only 56 are Server 2008 or newer. Now I had a third criteria:
3) My script needs to work against Server 2000, 2003, 2008, 2008 R2
I did some searching, and asked around, and wasn’t getting very far – starting to get desperate, I enabled remote DCOM on a test machine, found out what registry keys it changed, and thought I’d use this:
(From Shay Levi’s "Stand alone registry functions library")
http://blogs.microsoft.co.il/blogs/scriptfanatic/archive/2007/10/30/stand-alone-registry-functions-library.aspx
- $a = Get-RegBinary <machine that works> localmachine software\microsoft\ole MachineLaunchRestriction
- $a | Export-Clixml C:\scripts\New\dcom.clixml
- $b = Import-Clixml C:\scripts\New\dcom.clixml\parSet-RegBinary <other server> localmachine software\microsoft\ole MachineLaunchRestriction $b
This has problems though – If the target server had other than default DCOM permissions I’d overwrite them.
For the second part of the problem, enabling remote WMI, I thought I found a solution on this blog:
https://blogs.msdn.com/wmi/archive/2009/07/27/scripting-wmi-namespace-security-part-3-of-3.aspx – I mean how perfect, “Scripting WMI Namespace Security” right?
Well, it turns out that I needed to read this on Part 1 of his post: “In each namespace, there is a singleton instance that controls namespace security: __SystemSecurity=@. It exposes several methods, but in this case, we are only interested in two (available on Vista+): GetSecurityDescriptor and SetSecurityDescriptor. 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.”
Wow – Bummer. But, I don’t give up easily. So, off to Bing some more. I DID find a C# project on “The Code Project” called WMI Namespace Security, (http://www.codeproject.com/KB/system/WmiSecurity.aspx) and when I downloaded and compiled it, it granted the permissions I needed. However, the groups that own the servers wanted a script, not a compiled program. They wanted to see what the script was doing, and besides, I wanted to do it in PowerShell!
But, between Steve Lee’s blog on Scripting WMI Namespace Security, and J_Madden’s C# program, I had some clues.
I searched the web for information and found this:
http://msdn.microsoft.com/en-us/library/ms735120.aspx
Close, but I don’t want to add the built in users group.
I searched for a method to retrieve a SID and found Adam Bell’s excellent script on:
http://www.leadfollowmove.com/archives/powershell/security-identifiers-sids-and-nt-account-name
Now I have a SID, I know what registry value I need to modify for the DCOM permissions, and what I have to do to set WMI permissions, so I need to read and write registry values. I could have continued to use Shay’s excellent functions from above, but found this page:
http://www.vistax64.com/powershell/23050-powershell-stdregprov.html
OK, I’m starting to tie it all together, and have a working script in 31 lines of PowerShell:
- function get-sid
- {
- Param (
- $DSIdentity
- )
- $ID = new-object System.Security.Principal.NTAccount($DSIdentity)
- return $ID.Translate( [System.Security.Principal.SecurityIdentifier] ).toString()
- }
- $sid = get-sid "local\maxsmart"
- $SDDL = "A;;CCWP;;;$sid"
- $DCOMSDDL = "A;;CCDCRP;;;$sid"
- $computers = Get-Content "computers.txt"
- foreach ($strcomputer in $computers)
- {
- $Reg = [WMIClass]"\\$strcomputer\root\default:StdRegProv"
- $DCOM = $Reg.GetBinaryValue(2147483650,"software\microsoft\ole","MachineLaunchRestriction").uValue
- $security = Get-WmiObject -ComputerName $strcomputer -Namespace root/cimv2 -Class __SystemSecurity
- $converter = new-object system.management.ManagementClass Win32_SecurityDescriptorHelper
- $binarySD = @($null)
- $result = $security.PsBase.InvokeMethod("GetSD",$binarySD)
- $outsddl = $converter.BinarySDToSDDL($binarySD[0])
- $outDCOMSDDL = $converter.BinarySDToSDDL($DCOM)
- $newSDDL = $outsddl.SDDL += "(" + $SDDL + ")"
- $newDCOMSDDL = $outDCOMSDDL.SDDL += "(" + $DCOMSDDL + ")"
- $WMIbinarySD = $converter.SDDLToBinarySD($newSDDL)
- $WMIconvertedPermissions = ,$WMIbinarySD.BinarySD
- $DCOMbinarySD = $converter.SDDLToBinarySD($newDCOMSDDL)
- $DCOMconvertedPermissions = ,$DCOMbinarySD.BinarySD
- $result = $security.PsBase.InvokeMethod("SetSD",$WMIconvertedPermissions)
- $result = $Reg.SetBinaryValue(2147483650,"software\microsoft\ole","MachineLaunchRestriction", $DCOMbinarySD.binarySD)
- }
#1 by Stuat Coney on November 21, 2009 - 15:59
great work on this, this is a match for a requirement we have today (and have like yourself at one point) got to compiling source code to get a solution – in our case it was some source examples from the MS SDK. Butt like yourself we want a scripted solution.
thanks again – and good luck with your new blog! – Stu.
#2 by Patrick Dunnigan on November 23, 2009 - 17:37
This is exactly what I was looking for. Works perfectly as is. I have modified it to process multiple namespaces in another loop since root\rsop does not inherit from root. I’ll also probably put some error handling and verbose logging but this is the perfect start.
MS should add this to their admin scripts, I can’t believe this isn’t a much larger problem that you solved here.
Thanks,
Patrick
#3 by Brad Turner on June 12, 2010 - 23:50
This was an excellent compilation – I was able to use your approach to buld something suited to applying DCOM and WMI permissions to FIM 2010 implementations. I will post my scripts soon on the blog!
#4 by karlmitschke on June 14, 2010 - 17:25
Thanks, Brad 🙂
#5 by Brad Turner on June 13, 2010 - 03:26
The links to the scripts are posted here:
http://www.identitychaos.com/2010/06/fim-2010-using-powershell-to-set-dcom.html
#6 by karlmitschke on June 14, 2010 - 18:43
Thanks again, Brad – if I ever need FIM scripts, i will know where to go 🙂
#7 by Dan on August 8, 2010 - 14:40
Hi Karl,
Does this script run on one server and reach out to all the servers listed in computers.txt to enable WMI on each server –or– is this script supposed to be run on each server?
If this runs on each server, how did you proceed to get Powershell on each server to run the script? (I believe it is only pre-installed in 2008).
If its just a run once script, did you just populate computers.txt then run the script?
#8 by karlmitschke on August 9, 2010 - 19:15
Dan;
I run this from my worstation against the servers listed in computers.txt.
Most of those server do not have PowerShell installed.
The script does not rely on PowerShell Remoting, so no remote PowerShell is needed.
Yes, I did just populate computers.txt and run the script.
Karl
#9 by Rui Covelo on December 3, 2010 - 05:46
Thanks for this post. Didn’t solve my problem but I learned a lot. I’m stuck with a Windows 2003 server and so I don’t have Win32_SecurityDescriptorHelper which means I don’t know yet how to interpret it and changed it. My search continues.
For accessing DCOM, I just added my user to “Distributed COM Users”
Thanks again.
#10 by Karl Mitschke on December 3, 2010 - 06:50
Glad you found it (somewhat) useful
#11 by Rui Covelo on December 3, 2010 - 07:04
I’m almost there. I’m using some classes from System.Security namespace 🙂
I’ll post here when I have it completely done.
#12 by Karl Mitschke on December 3, 2010 - 12:13
OK, Thanks 🙂
#13 by alex on October 28, 2011 - 00:19
did you get this working finally on windows 2003 server?
#14 by Karl Mitschke on October 31, 2011 - 13:56
Yes, it worked fine on Server 2003.
#15 by alex on November 1, 2011 - 22:30
Win32_SecurityDescriptorHelper is not available on Windows server 2003. can you please explain how you made it to work on Windows 2003?
did u use some different class?
#16 by Karl Mitschke on November 2, 2011 - 08:10
Run the script FROM a Server 2008 or Windows Vista or above workstation…
#17 by Hans-Peter Harpøth on March 22, 2012 - 06:27
Great work. Great script.
Will it work with a domain group instead of a domain user ?
#18 by Karl Mitschke on March 23, 2012 - 08:12
Thanks.
This should work with a security group instead of a user.
Karl
#19 by jvierra on May 1, 2012 - 08:55
WS2008R2 now, finally, includes editors for this.
For DCOM consider using Group Policy. The GP settings have been around since WS2003 or XP SP2 for XP.
Also see:
http://social.technet.microsoft.com/Forums/en-US/winserverGP/thread/2dc28d2a-04dd-4c94-a786-389261f78794
From the Microsoft Management Infrastructure blog and Group Policy Forum
#20 by Angelo on May 30, 2012 - 02:09
Is it possible to add the account on the “root” and make it propagate on the subnamespaces inside it?
#21 by Karl Mitschke on June 29, 2012 - 08:28
I’d have to test this, but I don’t see why not.
Karl
#22 by Juergen on August 6, 2012 - 01:40
Hello Karl,
thank you for your script.
I want to know which values in the script do i have to set to my own values?
computers.txt -> list of computernames that i want to query
local\maxsmart -> is this the non-admin-domain-account ??
$DSIdentity -> what is this? do i have to set it?
Regards,
Jueregn
#23 by Karl Mitschke on August 6, 2012 - 07:48
Jueregn;
Thanks for the comments 🙂
computers.txt Is the list of computernames that you want to query
local\maxsmart IS this the non-admin-domain-account you wish to grant access to.
$DSIdentity < This is actually set by running $sid = get-sid "local\maxsmart" – So, you don't have to worry about it.
#24 by Juergen on August 7, 2012 - 00:27
Thank you Karl,
if i run the script it gets executed without errors.
So everything seems fine….
But if i try to query wmi with my user i get no result:
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Alle Rechte vorbehalten.
C:\Windows>WMIC /Node:PC1120 ComputerSystem Get UserName
UserName
C:\Windows>
Do i have to do something else if i want to get the UserName which is currently logged on….?
#25 by Martijn on November 23, 2012 - 03:36
Thank you. Great script, huge timesaver!
#26 by Kevin on December 8, 2012 - 20:14
Hi this looks to be a good script, but I cannot get it to run without errors.
Exception calling “Translate” with “1” argument(s): “Some or all identity references could not be translated.”
At D:\Kevin\Desktop\test.ps1:5 char:21
+ return $ID.Translate <<<< ( [System.Security.Principal.SecurityIdentifier] ).toString()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Exception calling "InvokeMethod" with "2" argument(s): "Invalid parameter "
At D:\Kevin\Desktop\test.ps1:27 char:44
+ $result = $security.PsBase.InvokeMethod <<<< ("SetSD",$WMIconvertedPermissions)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
#27 by Karl Mitschke on December 31, 2012 - 10:16
Hello;
Is the account on line 9 a valid domain account?
You need to modify $sid = get-sid “local\maxsmart”
Karl
#28 by Tim Shirey on February 6, 2013 - 12:05
Karl,
Thanks so much for the script. I am going to use it as part of server deployment, so I modified it to get the local machine name. I did have one question, however.
When I run this against my Win7 machine for testing – the “Lauch and Activation Permission” seems to only be setting “Local Launch” and “Remote Activation” permissions.
How can I get it to set all four permissions (adding ‘Remote” Launch” and “Local Activation”)?
#29 by Karl Mitschke on February 15, 2013 - 14:14
Tim;
You should be able to do this by modifying line 11 to
$DCOMSDDL = “A;;CCDCLCSWRP;;;$sid”
Karl