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.

Reply via email to