Posts RSS Comments RSS 253 Posts and 411 Comments till now

Archive for the '.NET' Category

Perhaps Brain should use Powershell (It is taking over the world)

Just listen to a presentation on what the Cluster team is doing with R2… it is amazing.

Here is a little preview
PowerShell for Failover Clustering in Windows Server 2008 R2

Get/Set-ADACL (ACL and SDDLs for Active Directory!)

A friend had a need to get/set Active Directory ACLs. So I wrote these.

They will use [System.DirectoryServices.ActiveDirectoryAccessRule] objects or SDDLs strings.

Note: I put the .NET classes and MS Spec for SDDLs at the bottom. Dont miss it!

Get-ADACL.ps1

# Get-ADACL.ps1
Param($DNPath,[switch]$SDDL,[switch]$help,[switch]$verbose)
function HelpMe{
    Write-Host
    Write-Host " Get-ADACL.ps1:" -fore Green
    Write-Host "   Gets ACL object or SDDL for AD Object"
    Write-Host
    Write-Host " Parameters:" -fore Green
    Write-Host "   -DNPath                : Parameter: DN of Object"
    Write-Host "   -sddl                  : [SWITCH]:  Output SDDL instead of ACL Object"
    Write-Host "   -Verbose               : [SWITCH]:  Enables Verbose Output"
    Write-Host "   -Help                  : [SWITCH]:  Displays This"
    Write-Host
    Write-Host " Examples:" -fore Green
    Write-Host "   Get ACL for ‘cn=users,dc=corp,dc=lab’" -fore White
    Write-Host "     .\Get-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’" -fore Yellow
    Write-Host "   Get SDDL for ‘cn=users,dc=corp,dc=lab’" -fore White
    Write-Host "     .\Get-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -sddl " -fore Yellow
    Write-Host
}

if(!$DNPath -or $help){HelpMe;return}

Write-Host
if($verbose){$verbosepreference="continue"}

Write-Verbose " + Processing Object [$DNPath]"
$DE = [ADSI]"LDAP://$DNPath"

Write-Verbose "   – Getting ACL"
$acl = $DE.psbase.ObjectSecurity
if($SDDL)
{
    Write-Verbose "   – Returning SDDL"
    $acl.GetSecurityDescriptorSddlForm([System.Security.AccessControl.AccessControlSections]::All)
}
else
{
    Write-Verbose "   – Returning ACL Object [System.DirectoryServices.ActiveDirectoryAccessRule]"
    $acl.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier])
}

Set-ADACL.ps1

# Set-ADACL.ps1
Param($DNPath,$acl,$sddl,[switch]$verbose,[switch]$help)
function HelpMe{
    Write-Host
    Write-Host " Set-ADACL.ps1:" -fore Green
    Write-Host "   Sets the AD Object ACL to ‘ACL Object’ or ‘SDDL’ String"
    Write-Host
    Write-Host " Parameters:" -fore Green
    Write-Host "   -DNPath                : Parameter: DN of Object"
    Write-Host "   -ACL                   : Parameter: ACL Object"
    Write-Host "   -sddl                  : Parameter: SDDL String"
    Write-Host "   -Verbose               : [SWITCH]:  Enables Verbose Output"
    Write-Host "   -Help                  : [SWITCH]:  Displays This"
    Write-Host
    Write-Host " Examples:" -fore Green
    Write-Host "   Set ACL on ‘cn=users,dc=corp,dc=lab’ using ACL Object" -fore White
    Write-Host "     .\Set-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -ACL $acl" -fore Yellow
    Write-Host "   Set ACL on ‘cn=users,dc=corp,dc=lab’ using SDDL" -fore White
    Write-Host "     .\Set-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -sddl `$mysddl" -fore Yellow
    Write-Host
}

if(!$DNPath -or (!$acl -and !$sddl) -or $help){HelpMe;Return}

Write-Host
if($verbose){$verbosepreference="continue"}
Write-Verbose " + Processing Object [$DNPath]"

$DE = [ADSI]"LDAP://$DNPath"
if($sddl)
{
    Write-Verbose "   – Setting ACL using SDDL [$sddl]"
    $DE.psbase.ObjectSecurity.SetSecurityDescriptorSddlForm($sddl)
}
else
{
    foreach($ace in $acl)
    {
        Write-Verbose "   – Adding Permission [$($ace.ActiveDirectoryRights)] to [$($ace.IdentityReference)]"
        $DE.psbase.ObjectSecurity.SetAccessRule($ace)
    }
}
$DE.psbase.commitchanges()
Write-Host

More Info
I used the following .NET Classes
System.DirectoryServices.DirectoryEntry
http://msdn2.microsoft.com/en-us/library/system.directoryservices.directoryentry.aspx
System.DirectoryServices.ActiveDirectoryAccessRule
http://msdn2.microsoft.com/en-us/library/system.directoryservices.activedirectoryaccessrule.aspx
System.DirectoryServices.ActiveDirectorySecurity
http://msdn2.microsoft.com/en-us/library/system.directoryservices.activedirectorysecurity.aspx
System.Security.AccessControl.AccessControlSections
http://msdn2.microsoft.com/en-us/library/system.security.accesscontrol.accesscontrolsections(vs.80).aspx

SDDL Info
MS: http://msdn2.microsoft.com/en-us/library/aa379567.aspx

Lab Build w/ Quest tools (coming soon!)

A user (valdezdj) pointed out that my build script would be much shorter if I had used the Quest tools.

I agree and have always planned on releasing a Quest version. I just haven’t had time 🙁

I love the Quest tools, but there are tons of people at there that don’t have the ability to use them. This is why usually only post “Powershell Only” examples. In this case I planned on posting both to show the power of the Quest CMDLets.

To Download Quest AD CMDLets: http://www.quest.com/powershell/

p.s. This is challenge… you guys and post code to 🙂

Get-InstalledSoftware (what software is installed?)

nishant left a comment on my “Powershell, Remote Registry and You! Part 1 (Overview)” post.

Nishant asked “I want a complete list of software installed on a remote machine using Powershell.”

I decided to post on this because it is brought up a lot. Unfortunately, getting a complete list of install applications is not that straightforward. Some applications do not store information in the Registry so the best we can do is list software that provides Uninstall regkey info. That is what the script below does. While this is not perfect it does a pretty good job getting the info.

Param($srv=$env:ComputerName)
$regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Srv)
$key = $regKey.OpenSubkey("Software\Microsoft\Windows\CurrentVersion\Uninstall",$false)
$key.GetSubKeyNames()

It is common to see people use Win32_Product to list installed apps, but it is important to point out this only list applications installed by an MSI installer.

Perhaps the best options is to combine both of these options.

# Get-InstalledSoftware
Param($srv=$env:ComputerName)
$regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$Srv)
$key = $regKey.OpenSubkey("Software\Microsoft\Windows\CurrentVersion\Uninstall",$false)
Write-Host
Write-Host "Getting Software in Uninstall Key" -Fore Green
Write-Host ("-"*60) -Fore Gray
$key.GetSubKeyNames()
Write-Host
Write-Host "Getting Software From Win32_Product" -Fore Green
Write-Host ("-"*60) -Fore Gray
get-wmiobject Win32_Product -comp $srv | foreach{$_.Name}
Write-Host

Converting Secure String

I recently heard a question about ConvertTO-SecureString and ConvertFrom-SecureString.

These CMDLets may be a little confusing so let’s talk a little about them.

Before you read the rest, it is key to understand what a Secure String actually is.

A Secure String is simple text that is encrypted in memory. This lets you store passwords or other secure data in memory without having to concern yourself with someone snooping your session or dumping your memory contents to get your data.

More Info Here
http://msdn2.microsoft.com/en-us/library/system.security.securestring.aspx

ConvertTo-SecureString: http://technet.microsoft.com/en-us/library/bb978707.aspx
I think this where it gets a little confusing for people. ConvertTo is meant to take an encrypted string and store it as a Secure String. Specificially for the output from ConvertFrom-SecureString. It will allow you use -asPlainText w/ -Force if you just want to convert a piece of text to Secure String.

ConvertFrom-SecureString: http://technet.microsoft.com/en-us/library/bb978629.aspx
This is used to convert a Secure String to text. It is import to note…. this is NOT the original text but the string representation of the encryption. This is a great tool for exporting the Secure String to a file in an encrypted form.

One other note before I show the code. By default (unless you provide a key) it uses Windows Data Protection API (DPAPI). This is VERY important. This process it is fairly secure, but can only decrypted by you on that specific machine. On the flip side… using a Key is not near as secure and some would argue security by obscurity.

Now… lets look at what we have… nothing magic here. We have three functions Export-EncryptedText. Import-EncryptedText, and Get-EncryptedText.
Export-EncryptedText: This gets a Secure String and exports to a file
Import-EncryptedText: This imports a Secure String Text from a file and returns a Secure String
Get-EncryptedText: This converts a Secure String into the orginal Text

We use “Read-Host -AsSecureString” to create convert our text to a secure string.

function Export-EncryptedText{
    param($text,$file,$key)
    if($key){ConvertFrom-SecureString -SecureString $text -key $key | out-file $file}
    else{ConvertFrom-SecureString -SecureString $text | out-file $file}
}
function Import-EncryptedText{
    Param($file,$key)
    $textFromFile = Get-Content $file
    if($key){ConvertTO-SecureString $TextFromFile -key $key}
    else{ConvertTO-SecureString $TextFromFile}
}
function Get-EncryptedText($text) {
    $Ptr = [System.Runtime.InteropServices.Marshal ]::SecureStringToCoTaskMemUnicode($text)
    $result = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)
    [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($Ptr)
    $result
}

# Example Use
$dataToStore = Read-Host -AsSecureString
"My String To Encrypt Here"

# Key… needs to be a 16, 24, or 32 byte array
$key = (200..231)

# Use this to put the password in a file
Export-EncryptedText -text $dataToStore -file c:\data\testfile.secure -key $key

# To get the password back you do this
$myText = Import-EncryptedText c:\data\testfile.secure -key $key

# To see the text use Get-SecurePass
Get-EncryptedText $myText

Error Handling: Part1 – $Error Objects

Error handling is one thing that is not discussed as much as it should be. It is one the most important parts of working with any scripting language and in this case the interactive nature of Powershell. We have had much discussion in the IRC channel over the last couple of weeks, discussing this exact topic. So I thought I should do my best to post what I have gleaned from the discussion.

This is one of at least a two part (maybe Three) series.
* Error Handling: Part1 – $Error Objects
* Error Handling: Part2 – Throw “A word about Trapping”
* Error Handling: Part3 – The Power of Set-PSDebug

Ask anyone that writes scripts. The ratio for Error correction in code is INSANE. Its almost 2/1. A 10 line script soon becomes 30 and a simple script becomes insanely complicated add to this the interactive nature of Powershell and you have the potential for many headaches. While Error handling in Powershell is far superior than anything to date… it still has some qwerks. I will do my best to cover some of these. Before we proceed from here, It is VERY important that you understand scope. I started to write this up, but to be honest, I think the Powershell team did a great job here. Please take some time to read the about file for scope.
PS> get-help about_Scope

Now, lets start off with some of the basics:

There are several built-in objects in powershell that control and provide information about Errors
$Error
> This is a object that stores an array of RICH Error objects
> Type: System.Management.Automation.ErrorRecord
$MaximumErrorCount:
> Total number of ErrorRecord objects that will be saved in $error
$LASTEXITCODE
> Exit Code of the Last script or native command than ran (i.e. ping.exe)
> This does NOT include cmdlets
$?
> Boolean Value that is $false if an error occured on the last command.
$ErrorActionPreference
> Determines the Default action if error is thrown
> Type System.Management.Automation.ActionPreference
> Continue: Outputs error, but keeps processing
> SilentlyContinue: No output and it keeps going
> Inquire: Prompt user for action
> Stop: Outputs error and halts processing
$ErrorView
> This is how the error will output to the screen
> This truly is a variable and can contain anything, but will use NormalView if anything other than CategoryView is the value
> NormalView: Default. Outputs semi-verbose description,Line Number, and character
> CategoryView: Only displayes the category the error is in.

Now that we have an overview of the objects that make up the error environment in Powershell lets deep dive into a couple of the more important ones.

$Error: This is by far the object with the most amount of data… it really does contain pretty much everything you could possibly want to know about an error. It is important to realize that this is truly an object. It has properties, methods, and even a Type just as all the other objects have. You can see this by piping $error to get-member

PS> $error | get-member
   TypeName: System.Management.Automation.ErrorRecord

Name                      MemberType Definition
====                      ========   ========
Equals                    Method     System.Boolean Equals(Object obj)
GetHashCode               Method     System.Int32 GetHashCode()
GetObjectData             Method     System.Void GetObjectData(SerializationInfo info, StreamingContext context)
GetType                   Method     System.Type GetType()
get_CategoryInfo          Method     System.Management.Automation.ErrorCategoryInfo get_CategoryInfo()
get_ErrorDetails          Method     System.Management.Automation.ErrorDetails get_ErrorDetails()
get_Exception             Method     System.Exception get_Exception()
get_FullyQualifiedErrorId Method     System.String get_FullyQualifiedErrorId()
get_InvocationInfo        Method     System.Management.Automation.InvocationInfo get_InvocationInfo()
get_TargetObject          Method     System.Object get_TargetObject()
set_ErrorDetails          Method     System.Void set_ErrorDetails(ErrorDetails value)
ToString                  Method     System.String ToString()
CategoryInfo              Property   System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}
ErrorDetails              Property   System.Management.Automation.ErrorDetails ErrorDetails {get;set;}
Exception                 Property   System.Exception Exception {get;}
FullyQualifiedErrorId     Property   System.String FullyQualifiedErrorId {get;}
InvocationInfo            Property   System.Management.Automation.InvocationInfo InvocationInfo {get;}
TargetObject              Property   System.Object TargetObject {get;}

Note: Because $error is an array it inherits from [System.Array] and its methods. Clear() is the most notable.

I could probably do a whole post on just $error, but that is beyond the scope of this one so I want just cover a few properties and one method
ErrorDetails
> This provides the Details of the Error. I wanted to note this is often blank
Exception
> This is the actual exception that was thrown
> This is an object so it will have properties and methods of its own.
TargetObject
> This is the object that operation was being performed when the error occured.
InvocationInfo
> This contains detail information about the command that was run (i.e. Line Number, CMDlet, and Position)
Clear()
> This method clears the $error array.

More Info: http://msdn2.microsoft.com/en-us/library/system.management.automation.errorrecord.aspx

$ErrorActionPreference: I would say this is the second most important object for error handling. I have personally seen this question posted at least 10 times on the News Groups, “What is the powershell equivalent to “On Error Resume Next.” The answer lies in this object more specifically by setting $ErrorActionPreference to “SilentlyContinue.” One thing I would like to point out about this particular object is that it is scope specific. This is very important to understand. Learning to control error action at the lowest level is key to writing good scripts and knowing the state of this object can save you a ton of heartache. I cant tell you how many times I beat my head against the wall trying to figure out why I wasnt getting any error information… hmmm, $erroractionpreference set to “SilentlyContinue,” maybe that is the problem.

More Info Here: http://msdn2.microsoft.com/en-us/library/system.management.automation.actionpreference.aspx

In Summary, I think this a good overview of the Error Variables/Objects that you have access to, but it really only scratches the surface. I would suggest spending some time on MSDN looking through system.management.automation Namespace and get a good idea of all the information there.

Test-Port (kinda like portqry without verbose output)

We had a little dicussion on www.powershelllive.com forums about the most efficient way to Test a machine before trying a WMI query against it (as it has a log timeout.) My first suggestion was to use a ping (WMI style) but Jeff from http://blog.sapien.com brought up a valid point… what if ICMP is NOT Allowed…

Enter Test-Port. This nifty little script uses the TCPClient Class to test connectivity. Stay tuned as I am planning some mods.

Test-Port
– Takes parameter $srv for Server Name
– Takes Parameter for Port, Defaults to 135 for RPC mapper.
– Takes Timeout.. defaults to 3000 (miliseconds)
– If it cannot connect within timeout… Returns $false
– If it gets exception connecting to port… Returns $false
– If it connects… Returns $True

function Test-Port{
    Param([string]$srv,$port=135,$timeout=3000,[switch]$verbose)
    $ErrorActionPreference = "SilentlyContinue"
    $tcpclient = new-Object system.Net.Sockets.TcpClient
    $iar = $tcpclient.BeginConnect($srv,$port,$null,$null)
    $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
    if(!$wait)
    {
        $tcpclient.Close()
        if($verbose){Write-Host "Connection Timeout"}
        Return $false
    }
    else
    {
        $error.Clear()
        $tcpclient.EndConnect($iar) | out-Null
        if($error[0]){if($verbose){write-host $error[0]};$failed = $true}
        $tcpclient.Close()
    }
    if($failed){return $false}else{return $true}
}

Backup-EventLog

## UPDATED… ADDED a EVT format Script as Well ##

I saw a post on EE that backups up the eventlogs on Server using VBScript… I wanted to see what I could do with Powershell and this is what I came up with. I put this together pretty quick, so not much Error checking or anything, but the vbscript was 207 lines long without comments.

Basically it does the following
– Takes a BackupLoccation as a Parameter
– Takes a List file or a -FromAD switch
– List gets the computers from a file
– FromAD gets computers from AD
– Creates a Backup folder Named -Logs-
– Like Server1-Logs-09110750
– Processes Each Event Log and backs up to a File
– Clears Log
– I put the output of the script on the bottom.
– NOTE: Security Logs Take awhile. I assume this because I am Generating Events by Reading the Log.

Param($BackupLocation,$list,$FromAD)
function Get-ADComputers{
    $filter = "(&(objectcategory=computer))"
    $root = [ADSI]""
    $props = "dNSHostName","sAMAccountName"
    $Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
    $Searcher.PageSize = 1000
    $Computers = $Searcher.findAll() | %{$_.properties[‘dnshostname’]}
    $Computers
}
function Ping-Server {
   Param([string]$server)
   $pingresult = Get-WmiObject win32_pingstatus -f "address=’$Server’"
   if($pingresult.statuscode -eq 0) {$true} else {$false}
}

if($FromAD){$computers = Get-ADComputers}
else{if($list){$computers = get-Content $list}else{Write-Host "Please Provide List";return}}

foreach($computer in $computers)
{
    $Folder = "{2}\{1}-Logs-{0:MMddyymm}" -f [DateTime]::now,$computer,$backupLocation
    Write-Host "+ Processing Server $Computer"
    new-Item $folder -type Directory -force  | out-Null

    if(Ping-Server $computer)
    {
        Write-Host "  + Created Backup Folder $folder"
        $eventlogs = [System.Diagnostics.EventLog]::GetEventLogs($computer)
        foreach($log in $eventlogs)
        {
            $LogFile = "{0}\{1}.csv" -f $Folder,$log.Log
            Write-Host "  + Processing $($log.Log) Log"
            Write-Host "    – Backing up $($log.Log)"
            $logEntries = $log.Entries | %{"{0},{1},{2},{3},{4}" -f $_.TimeGenerated,$_.EntryType,$_.Source,$_.EventID,$_.Message}
            $logEntries | out-File $LogFile -enc ASCII -width 500
            Write-Host "    – Backed up to $logFile"
            Write-Host "    – Clearing Log $($Log.Log)"
            $log.Clear()
        }
        Write-Host
    }
    else
    {
        Write-Host "Server $Computer failed PING!" -foregroundcolor red
    }
}

For those that perfer EVT format and WMI…. I left the Clear part commented

Param($BackupLocation,$list,$FromAD)

function Get-ADComputers{
    $filter = "(&(objectcategory=computer))"
    $root = [ADSI]""
    $props = "dNSHostName","sAMAccountName"
    $Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
    $Searcher.PageSize = 1000
    $Computers = $Searcher.findAll() | %{$_.properties[‘dnshostname’]}
    $Computers
}
function Ping-Server {
   Param([string]$server)
   $pingresult = Get-WmiObject win32_pingstatus -f "address=’$Server’"
   if($pingresult.statuscode -eq 0) {$true} else {$false}
}

if($FromAD){$computers = Get-ADComputers}
else{if($list){$computers = get-Content $list}else{Write-Host "Please Provide List";return}}

foreach($computer in $computers)
{
    if(ping-server $computer)
    {
        $Folder = "{1}-Logs-{0:MMddyymm}" -f [DateTime]::now,$computer
        Write-Host "+ Processing Server $Computer"
        New-Item "$backupLocation\$folder" -type Directory -force  | out-Null
        If(!(Test-Path "\\$computer\c$\LogBackups")){New-Item "\\$computer\c$\LogBackups" -type Directory -force | out-Null}
        $Eventlogs = Get-WmiObject Win32_NTEventLogFile -ComputerName $computer
        Foreach($log in $EventLogs)
        {
            $path = "\\{0}\c$\LogBackups\{1}.evt" -f $Computer,$log.LogFileName
            $result = ($log.BackupEventLog($path)).ReturnValue
            Copy-Item $path -dest "$backupLocation\$folder" -force
            #if($result -eq 0){$log.ClearEventLog()}
        }
    }
}

NOTE: Shortly after writing this… I found this little tibit… it seems to have been around since SP3 of Win2000

Found it Here
http://blogs.msdn.com/spatdsg/default.aspx

AutoBackupLogFiles – backs up the event logs “Using this entry causes the Event Log service to automatically clear a full event log and to back up the log file. ”
http://support.microsoft.com/kb/312571

The Power of -f. The Format Operator

This is from a NG post discussing the -f Operator… I thought it was a pretty good description. Compliments to Kiron.

Q: What is the -f operator

A: PowerShell’s Format Operator ‘-f’ is equivalent to .Net’s Composite
Formatting. The syntax is:
{index[,alignment][:formatString]} -f listOfValues

Composite Formatting
http://msdn2.microsoft.com/en-us/library/txafckwd(vs.71).aspx

Quoting from about_operator:
Format Operator “-f”
The format operator provides support for formatting strings via the .NET
string object format method. On the left hand side of the operator is the
format string and on the right hand of the operator is the collection of
objects to be formatted.
The following example shows some of the capabilities of the format operator.
PS> “{0} {1,-10} {2:N}” -f 1,”hello”,[math]::pi
1 hello 3.14

Some samples:

# the ‘|’ is for demonstrating the alignment
"{0:(###) ###-####}" -f 2224445555 # phone number
"{0:hh:mm:ss tt}" -f (get-date) # time, 12 hour format
"{0:HH:mm:ss}" -f (get-date) # time, 24 hour format
"{0:p4}" -f (1/3) # percent with four decimal places
"{0:c2}" -f (1724.87 * 12) # currency with two decimal places
"|{0,22:c2}|" -f (1724.87 * 12) # right aligned currency with two decimal places
"|{0,-22:n2}|" -f (13/57) # left aligned number with two decimal places
"{0:MM dd yy}" -f (get-date) # date, custom short format
"{0:MMMM yyyy}" -f (get-date) # date, ‘month year’ format
0..15 | % {"{0:0##}" -f $_} # filled number format
0..15 | % {"|{0,33}|" -f "Number $_"} # right aligned string
0..15 | % {"|{0,-33}|" -f "Number $_"} # left aligned string
0..15 | % {"|{0,33}|  |{0,-33}|" -f "Number $_"} # right & left aligned string

Suggested Reading.

Standard Numeric Format Strings
http://msdn2.microsoft.com/en-us/library/dwhawy9k.aspx

Custom Numeric Format Strings
http://msdn2.microsoft.com/en-us/library/0c899ak8.aspx

Standard DateTime Format Strings
http://msdn2.microsoft.com/en-us/library/az4se3k1.aspx

Custom DateTime Format Strings
http://msdn2.microsoft.com/en-us/library/8kb3ddd4.aspx

Enumeration Format Strings
http://msdn2.microsoft.com/en-us/library/c3s1ez6e.aspx

Check out this blog from the PowerShell Team
http://blogs.msdn.com/powershell/archive/2006/06/16/634575.aspx

Dealing iADSLargeInteger in Powershell

I have spent some time recently working with attributes in AD that get returned as iADSLargeInteger. This has been problematic as Powershell does not natively support iADSLargeInteger. In my travels I have come across three different ways of getting this information (four if you consider using reflection,) but after much discussion on the NG (news group) and on IRC I think the best option is using System.DirectoryServices.DirectorySearcher.

Lets review the 4 options:

1) Use a thirdparty Snap-in that uses ActiveDS.dll like this one from MoW iADSLargeInteger Snap-in.

2) You can use VBScript Engine in Powershell Like this

function Convert-iADSLargeIntPropertyToInt64{
    param([string]$prop,[string]$ldapPath,[string]$server=([ADSI]"LDAP://rootdse").dnshostname,[switch]$verbose)
    $statement=@"
Set objType = getObject("
"LDAP://$server/$ldapPath"")
Set objProp = objType.Get("
$prop")
result = objProp.HighPart * (2^32) + objProp.LowPart
"
@
    if($verbose)
    {
        Write-Host "+ Running Convert on $server"
        Write-Host "  – Property  : $Prop"
        Write-Host "  – Path      : "$ldapPath`""
        Write-Host "  – Statement : $statement"
    }
    $vbs = new-object -com MSScriptControl.ScriptControl
    $vbs.language = ‘vbscript’
    $vbs.ExecuteStatement($statement)
    [int64]$value = $vbs.Eval("result")
    return $value
}
 

3) Use System.DirectoryServices.DirectorySearcher MDSN Link

function Get-iADSLargeIntFromSearcher{
    param([string]$LdapPath,[string]$attribute = $(throw "Attribute Required"),[string]$server,[switch]$date)
    if($server){$de = [ADSI]"LDAP://$server/$LdapPath"}
    else{$de = [ADSI]"LDAP://$LdapPath"}
    $return = new-object system.DirectoryServices.DirectorySearcher($de)
    $value = ($return.findone()).properties[$attribute.ToLower()]
    if( $date)
    {
        [datetime]::FromFileTime([int64]::Parse($value))
    }
    else
    {
        $value
    }
}

4) I wont go in to much detail using Reflection, but it is still valid option. Get more info HERE. The function below is a MoW version.

function ConvertADSLargeInteger([object]$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)

    return $lowPart + $highPart
}

Here is a list of attributes that are stored as iADSLargeInteger. I used the function at the bottom to generate this list.
—————–
accountExpires
aCSAggregateTokenRatePerUser
aCSAllocableRSVPBandwidth
pekKeyChangeInterval
aCSMaxAggregatePeakRatePerUser
aCSMaxPeakBandwidth
aCSMaxPeakBandwidthPerFlow
aCSMaxTokenBucketPerFlow
uSNChanged
aCSMaxTokenRatePerFlow
uSNCreated
aCSMaximumSDUSize
uSNDSALastObjRemoved
aCSMinimumDelayVariation
aCSMinimumLatency
uSNLastObjRem
aCSMinimumPolicedSize
uSNSource
aCSNonReservedMaxSDUSize
aCSNonReservedMinPolicedSize
aCSNonReservedPeakRate
aCSNonReservedTokenSize
aCSNonReservedTxLimit
aCSNonReservedTxSize
aCSPermissionBits
mS-SQL-Memory
mS-SQL-Status
mS-SQL-Size
lastBackupRestorationTime
lastContentIndexed
lastLogoff
lastLogon
lastLogonTimestamp
lastSetTime
badPasswordTime
builtinCreationTime
builtinModifiedCount
priorSetTime
msWMI-Int8Default
lockOutObservationWindow
msWMI-Int8Max
lockoutDuration
msWMI-Int8Min
msWMI-Int8ValidValues
lockoutTime
privilegeValue
lSACreationTime
lSAModifiedCount
proxyLifetime
machinePasswordChangeInterval
pwdLastSet
maxPwdAge
maxRenewAge
maxStorage
maxTicketAge
creationTime
rIDAllocationPool
rIDAvailablePool
rIDPreviousAllocationPool
minPwdAge
rIDUsedPool
minTicketAge
modifiedCount
modifiedCountAtLastProm
dhcpFlags
dhcpMaxKey
dhcpUniqueKey
dhcpUpdateTime
msDS-Cached-Membership-Time-Stamp
forceLogoff
timeRefresh
timeVolChange
——————-

function Get-iADSLargeIntegerAttribute{
    param([string]$domain)
    if($domain){$rootDSE = [ADSI]"LDAP://$Domain/rootDSE"}
    else{$rootDSE = [ADSI]"LDAP://rootDSE"}
    $schema  = [ADSI]"LDAP://$($rootDSE.schemaNamingContext)"
    $filter = "(&(objectclass=attributeschema)(attributesyntax=2.5.5.16)(omsyntax=65))"
    $props = ("ldapdisplayname")
    $dsearcher = new-Object System.DirectoryServices.DirectorySearcher($schema,$filter,$props)
    $dsearcher.findall() | %{Write-Output "$($_.Properties[‘ldapdisplayname’])"}
}
 

UPDated highlighting Code

Next »