Issue #17031 has been updated by Ethan Brown.
* To reiterate the obvious, there is a difference between a local user / SID vs a domain user / SID * There are a number of available options for [binding string syntax](http://msdn.microsoft.com/en-us/library/windows/desktop/aa772316\(v=vs.85\).aspx), but for practical reasons, the only viable option is to use `WinNT://` -- additional details follow. Note that as mentioned above, `\` is treated as an escape for a literal. Not mentioned in the MSDN docs is an additional `GC://` global catalog style binding string to "execute fast queries" * `WinNT://SID` and `WinNT://servername/SID` are invalid -- to find a user by SID, when limited to only `WinNT://` style bind paths, enumeration of the local groups / users is required, to perform a match on a given SID -- some VB code is available [here](http://www.tek-tips.com/viewthread.cfm?qid=1247882) * For clarification, the `WinNT://servername/username,user` bind syntax is overloaded. `servername` may either be a `host` or a `domain` * The syntax that *could* be used for SID lookup uses `LDAP://`, but this is not viable. * `LDAP://COMPUTER/<SID=XXX>`, where `XXX` is a hexadecimal representation of the raw SID byte array is one option. It may also take the form of `<SID=S-X-X-XX-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXX-XXX>` where this is a string generated by the `ConvertSidToStringSid` Win32 API call - see [Binding to an Object Using a SID](http://msdn.microsoft.com/en-us/library/windows/desktop/ms675562\(v=vs.85\).aspx). Also see [LDAP ADsPath](http://msdn.microsoft.com/en-us/library/windows/desktop/aa746384\(v=vs.85\).aspx) for the "official" MSDN documentation. * Yet another available bind syntax is `LDAP://servername/<GUID=XXX>` where `XXX` is either a string of 32 hex characters without punctuation OR a string of hex characters `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX` - see [Using objectGUID to Bind to an Object](http://msdn.microsoft.com/en-us/library/windows/desktop/ms677985\(v=vs.85\).aspx) for more information. * Neither of the `LDAP` style bind syntax options may be used, as they alway need a host, and in local testing `LDAP://./foo` and `LDAP://localhost/foo` do not work by default on machines that are not running a DC. In testing, a PowerShell style query like `[ADSI]"LDAP://localhost/<SID=S-1-5-18>" | Select *` does not error, but it also does not yield any results. When removing the host, `[ADSI]"LDAP://<SID=S-1-5-18>" | Select *` does error with `exception occurred while retrieving member "distinguishedName": "The specified domain either does not exist or could not be contacted"`. NOTE: Also attempted were non well-known SIDs as well (users local to a given test environment). * Additionally, binding by SID like the above, when it works, will only return the `distinguishedName` property (based on forum reports). To retrieve the entire AD object would require an additional refetch with an `LDAP://distinguishedName` type query (for instance, to build a `DOMAIN\Username` style name). * Well-known SIDs like SYSTEM (S-1-5-18), Local Service (S-1-5-19), Network Service (S-1-5-20) are not globally unique and would require a host prefix if used in any sort of comparison. * WIN32OLE objects returned by the `WinNT` sytanx with [connect](http://ruby-doc.org/stdlib-1.9.3/libdoc/win32ole/rdoc/WIN32OLE.html#method-c-connect) method expose an `objectSID` property that is an array of bytes in [octet string](http://msdn.microsoft.com/en-us/library/ms180873\(v=vs.80\).aspx) format. The [IADsUser docs](http://msdn.microsoft.com/en-us/library/windows/desktop/aa746340\(v=vs.85\).aspx) do not mention this as it is specifically a [WinNT Custom User Property](http://msdn.microsoft.com/en-us/library/windows/desktop/aa746535\(v=vs.85\).aspx). This array of bytes cannot be passed directly to the [Win32::Security::SID.new](http://rubydoc.info/gems/win32-security/0.2.3/Win32/Security/SID:initialize) we are using as only a binary string format is accepted. Conversion with Ruby's Array#pack method must be performed * So how to translate from a SID to a username when needed? * The Win32 [LookupAcocuntSid](http://msdn.microsoft.com/en-us/library/windows/desktop/aa379166\(v=vs.85\).aspx) function is automatically called within `Win32::Security::SID::new`, so opt to use this where possible. * There is some sample [Powershell code](http://blogs.technet.com/b/heyscriptingguy/archive/2010/10/12/use-powershell-to-translate-a-user-s-sid-to-an-active-directory-account-name.aspx) demonstrating the conversion of a SID to a user name. This relies heavily on some .NET Framework classes, particularly `SecurityIdentifier` which understands how to convert a byte array into a SID. [More Powershell examples here](http://technet.microsoft.com/en-us/library/ff730940.aspx) * Such a PowerShell example looks like `(New-Object System.Security.Principal.NTAccount("Administrator")).Translate([System.Security.Principal.SecurityIdentifier]) | Format-List` Ultimately, the end game here is to: * Allow a `DOMAIN\User` style syntax to be expressed properly * Prevent the addition of duplicate users to groups, which generates errors ---------------------------------------- Bug #17031: Can't add domain user account as a member of a local group https://projects.puppetlabs.com/issues/17031#change-97964 * Author: Josh Cooper * Status: Accepted * Priority: High * Assignee: Josh Cooper * Category: * Target version: 3.x * Affected Puppet version: 2.7.6 * Keywords: windows user group domain * Branch: ---------------------------------------- This is a common need when managing domain service accounts that need to be a member of the local Administrators account. I thought it would be resolved once #16581 was fixed, but there's a more fundamental issue with the group provider, so I'm filing this as a separate issue. First, it attempts to add members to the group using an ADSI path of `WinNT://WIN-QP47VOHA2P4/BIZARRO\albert,user`, but it needs to be `WinNT://WIN-QP47VOHA2P4/BIZARRO/albert,user` <pre> def add_members(*names) names.each do |name| native_group.Add(Puppet::Util::ADSI::User.uri(name)) end end </pre> It may be possible to just use the SID form `WinNT://<SID>` but I'm not sure if that will work in a non-domain environment. Second, when calculating whether the group's members are insync? it compares names: <pre> members_to_add = desired_members - current_members add_members(*members_to_add) </pre> However the ADSI provider returns current members as, e.g. `albert`. But since this doesn't match `BIZARRO\albert`, the provider will think the resource is out of sync and will attempt to re-add a user that is already a member of the group and fail: <pre> err: /Stage[main]//Group[Foobars]/members: change from albertAdministrator to BIZARRO\albert Administrator failed: Add OLE error code:80070562 in Active Directory The specified account name is already a member of the group. HRESULT error code:0x80020009 Exception occurred. </pre> Really, the group provider needs to compare the current vs desired SIDs to determine which users to add, similar to what we do in the file and scheduled_task providers. -- You have received this notification because you have either subscribed to it, or are involved in it. To change your notification preferences, please click here: http://projects.puppetlabs.com/my/account -- You received this message because you are subscribed to the Google Groups "Puppet Bugs" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/puppet-bugs. For more options, visit https://groups.google.com/groups/opt_out.
