none
PowerShell, LDAP, and the IADsLargeInteger Interface. RRS feed

  • Question

  • $s = [adsisearcher]'samaccountname=someuser'
    $r = $s.FindOne()
    $e = $r.GetDirectoryEntry()
    
    $e.Properties['lastlogontimestamp']

    Returns System.__COMObject.  I found a function that will take the value of the IADsLargeInteger data and convert it to an Int64 here: 

    function AdsLargeIntegerToInt64($adsLargeInteger) {
        [Int32]$highPart = $adsLargeInteger.GetType().InvokeMember("HighPart", [System.Reflection.BindingFlags]::GetProperty, $null, $adsLargeInteger, $null)
        [Int32]$lowPart  = $adsLargeInteger.GetType().InvokeMember("LowPart",  [System.Reflection.BindingFlags]::GetProperty, $null, $adsLargeInteger, $null)
        [Int64]("0x{0:x8}{1:x8}" -f $highPart, $lowpart)
    } 

    ...but I'm at a loss as to how do you write a value back to the property as, $e.Property['propertyname'].Value = 3450892348124 doesn't work. -- even when using .CommitChanges() and .RefreshCache() to try and verify the data.

    Any help would be appreciated.

    Wednesday, April 16, 2014 2:39 PM

Answers

  • Perhaps you mean something like this?


    function ConvertTo-IADSLargeInteger {
      param(
        [Int] $highPart,
        [Int] $lowPart
      )
      $adsLargeInteger = new-object -comobject LargeInteger
      [Void] $adsLargeInteger.GetType().InvokeMember("HighPart","SetProperty",$NULL,$adsLargeInteger,$highPart)
      [Void] $adsLargeInteger.GetType().InvokeMember("LowPart","SetProperty",$NULL,$adsLargeInteger,$lowPart)
      $adsLargeInteger
    }
    


    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by thepip3r Wednesday, April 16, 2014 6:04 PM
    Wednesday, April 16, 2014 5:25 PM
    Moderator
  • Here's a refinement that lets you pass an Int64 directly:


    function ConvertTo-IADSLargeInteger {
      param(
        [UInt64] $number
      )
      $byteArray = [System.BitConverter]::GetBytes($number)
      $highPart = [System.BitConverter]::ToInt32($byteArray, 4)
      $lowPart = [System.BitConverter]::ToInt32($byteArray, 0)
      $adsLargeInteger = new-object -comobject LargeInteger
      [Void] $adsLargeInteger.GetType().InvokeMember("HighPart","SetProperty",$NULL,$adsLargeInteger,$highPart)
      [Void] $adsLargeInteger.GetType().InvokeMember("LowPart","SetProperty",$NULL,$adsLargeInteger,$lowPart)
      $adsLargeInteger
    }
    


    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by thepip3r Wednesday, April 16, 2014 6:04 PM
    Wednesday, April 16, 2014 5:39 PM
    Moderator

All replies

  • Rather than GetDirectoryEntry(), I would retrieve lastLogonTimestamp as a part of your retrieved attributes, which returns the Int64 value. Then you can convert to local time using the DateTime object's FromFileTime static method. For example:


    $s = [ADSISearcher] "(sAMAccountName=someuser)"
    $s.SearchRoot = [ADSI] "LDAP://DC=fabrikam,DC=com"
    $s.PropertiesToLoad.AddRange(@("lastlogontimestamp"))
    $lastLogonTimestamp = $s.FindOne() | foreach-object {
      if ( $_.Properties["lastlogontimestamp"] ) {
        [DateTime]::FromFileTime($_.Properties["lastlogontimestamp"][0])
      }
    }
    


    -- Bill Stewart [Bill_Stewart]

    Wednesday, April 16, 2014 2:54 PM
    Moderator
  • Sure, and I know how to do that and I wish the DirectoryEntry were that easy but for some reason, it requires the IADsLargeInteger interface.  I have to use the DirectoryEntry object instead of the SearchResult object because I need to use the AuthenticationTypes property of the DirectoryEntry object for encryption.
    Wednesday, April 16, 2014 3:06 PM
  • Searching always helpful ('powershell iadslargeinteger'). Here's one result:

    http://bsonposh.com/archives/226

    Function listed there:


    function ConvertADSLargeInteger {
      param(
        $adsLargeInteger
      )
      $highPart = $adsLargeInteger.GetType().InvokeMember("HighPart","GetProperty",$NULL,$adsLargeInteger,$NULL)
      $lowPart  = $adsLargeInteger.GetType().InvokeMember("LowPart","GetProperty",$NULL,$adsLargeInteger,$NULL)
      $bytes = [System.BitConverter]::GetBytes($highPart)
      $tmp = [System.Byte[]]@(0,0,0,0,0,0,0,0)
      [System.Array]::Copy($bytes,0,$tmp,4,4)
      $highPart = [System.BitConverter]::ToInt64($tmp, 0)
      $bytes = [System.BitConverter]::GetBytes($lowPart)
      $lowPart = [System.BitConverter]::ToUInt32($bytes, 0)
      $lowPart + $highPart
    }
    

    I would note that he recommends DirectorySearcher as it's just simpler and more straightforward.

    -- Bill Stewart [Bill_Stewart]

    Wednesday, April 16, 2014 3:29 PM
    Moderator
  • I don't know if that was intentionally condescending or not but a converter is not what I was asking about.  What you have posted is a converter from IADsLargeInteger-to-Int64 (which I already have in my original post).  I'm asking how to write the value back as $e.Property['propname'] = $propvalue; $.CommitChange() doesn't work, or rather, will only accept a 32-bit value where a 64-bit value is what's expected.
    Wednesday, April 16, 2014 4:41 PM
  • What property do you want to write? lastLogonTimestamp should only be updated by the system.

    -- Bill Stewart [Bill_Stewart]

    Wednesday, April 16, 2014 4:54 PM
    Moderator
  • It is a custom, extended schema attribute in this organization that also uses an AD LargeInteger format (attributeSyntax:2.5.5.16).  They use it to store the date of an occurrence of an event in FILETIME format.  Thus, when I query it via the DirectoryEntry object, I get the same IADsLargeInteger interface problems associated with LastLogonTimestamp.  PowerShell returns a System.__ComObject which no usable properties or methods.  I found the converter which does successfully convert it to an Int64 which I then add 1600 days to, and I'm back to FILETIME.  But, now that I've done all of that, I need to update the value with a new time and have all of the pieces except being able to repopulate the value.
    Wednesday, April 16, 2014 5:10 PM
  • Perhaps you mean something like this?


    function ConvertTo-IADSLargeInteger {
      param(
        [Int] $highPart,
        [Int] $lowPart
      )
      $adsLargeInteger = new-object -comobject LargeInteger
      [Void] $adsLargeInteger.GetType().InvokeMember("HighPart","SetProperty",$NULL,$adsLargeInteger,$highPart)
      [Void] $adsLargeInteger.GetType().InvokeMember("LowPart","SetProperty",$NULL,$adsLargeInteger,$lowPart)
      $adsLargeInteger
    }
    


    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by thepip3r Wednesday, April 16, 2014 6:04 PM
    Wednesday, April 16, 2014 5:25 PM
    Moderator
  • Here's a refinement that lets you pass an Int64 directly:


    function ConvertTo-IADSLargeInteger {
      param(
        [UInt64] $number
      )
      $byteArray = [System.BitConverter]::GetBytes($number)
      $highPart = [System.BitConverter]::ToInt32($byteArray, 4)
      $lowPart = [System.BitConverter]::ToInt32($byteArray, 0)
      $adsLargeInteger = new-object -comobject LargeInteger
      [Void] $adsLargeInteger.GetType().InvokeMember("HighPart","SetProperty",$NULL,$adsLargeInteger,$highPart)
      [Void] $adsLargeInteger.GetType().InvokeMember("LowPart","SetProperty",$NULL,$adsLargeInteger,$lowPart)
      $adsLargeInteger
    }
    


    -- Bill Stewart [Bill_Stewart]

    • Marked as answer by thepip3r Wednesday, April 16, 2014 6:04 PM
    Wednesday, April 16, 2014 5:39 PM
    Moderator
  • Bill,

    Converting, assigning, and committing was the answer.  Thanks for pointing out that I had the pieces of the answer in front of me and thanks for the hand holding.

    Wednesday, April 16, 2014 6:04 PM
  • I updated the other function - this is shorter and a little more readable IMO:


    function ConvertFrom-IADSLargeInteger {
      param(
        [System.__ComObject] $adsLargeInteger
      )
      $highPart = $adsLargeInteger.GetType().InvokeMember("HighPart","GetProperty",$NULL,$adsLargeInteger,$NULL)
      $lowPart = $adsLargeInteger.GetType().InvokeMember("LowPart","GetProperty",$NULL,$adsLargeInteger,$NULL)
      $highBytes = [System.BitConverter]::GetBytes($highPart)
      $lowBytes = [System.BitConverter]::GetBytes($lowPart)
      [System.BitConverter]::ToUInt64($lowBytes + $highBytes, 0)
    }
    


    -- Bill Stewart [Bill_Stewart]

    Wednesday, April 16, 2014 6:14 PM
    Moderator