Posts RSS Comments RSS 117 Posts and 170 Comments till now

Archive for August, 2007

Powershell Crushes VBScript with AD query again

In my on going battle on EE to prove the value of powershell, I have destroyed yet another vbscript. While the vbscript look was impressive it was a monsterous 38 lines compaired to Powershell 12. Again with no short-cuts, you could trim another 4 lines with ease.

Here is my code. It gets all the groups and list the group name / member count / OU.

  1. $filter = "(&(objectcategory=group))"
  2. $root = [ADSI]""
  3. $dsSearcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter)
  4. $dsSearcher.PageSize = 1000
  5. $groups = $dsSearcher.findAll()
  6. @(foreach($group in $groups)
  7. {
  8.     [string]$name = $group.Properties.cn
  9.     [string]$count = ($group.psbase.properties.member).count
  10.     [string]$OU = ((($group.GetDirectoryEntry()).psbase).parent).distinguishedName
  11.     "GROUP:{0} Count:{1} OU:{2}" -f $name.PadRight(35),$count.padright(5),$ou
  12. }) | out-file C:\temp\yourfile.txt -enc ASCII

Output looks like this
GROUP:TGroup1 Count: OU:OU=MyGroups,DC=corp,DC=bb,DC=lab
GROUP:TGroup2 Count: OU:OU=MyGroups,DC=corp,DC=bb,DC=lab
GROUP:TGroup3 Count: OU:OU=MyGroups,DC=corp,DC=bb,DC=lab
GROUP:TGroup4 Count:1 OU:OU=MyGroups,DC=corp,DC=bb,DC=lab
GROUP:TGroup5 Count:2 OU:OU=MyGroups,DC=corp,DC=bb,DC=lab
….

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:

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

Script to set the Description of Computer Object

I recently joined Experts-Exchange to help people understand the glory of Powershell and how useful it can be to your average Server Admin with little to no scripting or developer experience and trying to sell the idea you do NOT have to be a Developer or hard core scripter to utilize the power that is at your finger tips.

In one of my 200+ response post in the last week :) An admin wanted to collect data from all the servers/workstations in his environment. What was so fun to watch was how Powershell DESTROYED vbscript and I didnt really take an shortcuts.

Total Line Count Vbscript = 98
Total Line Count Powershell = 53

Effectively what it does is collect hardware info from the server/workstation and updates AD Descript field with the data as well as export the info to .CSV file

CPU 3.20GHz/2GB Ram/69,845GB/1.44/CDD

Here is the code.
Download here http://bsonposh.com/uploads/wordpress/2007/08/Set-ComputerDescription.ps1

  1. $Servers = Get-Content "C:\computers.txt"
  2. $myobjects = @()
  3. foreach($server in $servers)
  4. {
  5.     write-host "Processing Server $server"
  6.     $query = "Select statuscode FROM Win32_PingStatus WHERE Address=’$server’ and Timeout=300"
  7.     $pingresult = Get-WmiObject -query $query
  8.     if($pingresult.statuscode -eq 0)
  9.     {
  10.         $myobj = "" | Select Name,CPU,Memory,DiskSpace,CD,Floppy
  11.         $myobj.Name = $server
  12.  
  13.         # Getting Computer Object
  14.         $filter = "(&(objectcategory=computer)(cn=$server))"
  15.         $ds = new-Object System.DirectoryServices.DirectorySearcher($filter)
  16.         $comp = ($ds.findone()).GetDirectoryEntry()
  17.  
  18.         # CPU info
  19.         $cpu = Get-WmiObject Win32_Processor -computername $server | select name -first 1 | foreach{$_.name}
  20.         $cpu = (($cpu -replace "Intel" -replace ‘\(R\)’).trim() -replace "Xeon\(TM\)").trim()
  21.         $myobj.CPU = $cpu
  22.  
  23.         # Memory
  24.         $mem = (Get-WmiObject Win32_OperatingSystem -computername $server).TotalVisibleMemorySize
  25.         $memory = $mem*1kb/1gb
  26.         $memory = "{0}GB" -f [int]$memory
  27.         $myobj.Memory = $memory
  28.  
  29.         # Disk
  30.         $disks = Get-WmiObject Win32_LogicalDisk -computername $server | Where-Object{$_.DriveType -eq 3}
  31.         $diskSz = (Get-WmiObject Win32_LogicalDisk -computername $server | Measure-Object -SUM size).SUM
  32.         $diskSize = $diskSz/1gb
  33.         $diskSize = "{0:n2}GB" -f $diskSize
  34.         $myobj.DiskSpace = $diskSize
  35.         if(Get-WmiObject Win32_FloppyDrive){$floppy = "1.44"}else{$floppy = "NoFloppy"}
  36.         if(Get-WmiObject Win32_CDROMDrive){$CD = "CDD"}else{$CD = "NO CD"}
  37.  
  38.         # Formating String and setting the Description Field on the Servers
  39.         $string = "{0}/{1} Ram/{2}/{3}/{4}" -f $cpu,$memory,$disksize,$floppy,$CD
  40.         $comp.psbase.invokeSet("Description",$string)
  41.         $comp.psbase.CommitChanges()
  42.         $myobj.CD = $cd
  43.         $myobj.floppy = $floppy
  44.  
  45.         # Adding custom object for export CSV
  46.         $myobjects += $myobj
  47.     }
  48.     else
  49.     {
  50.         Write-Host "$Server is NOT pingable"
  51.     }
  52. }
  53. $myobjects | export-csv C:\ComputerInfo.csv

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

  1. function Convert-iADSLargeIntPropertyToInt64{
  2.     param([string]$prop,[string]$ldapPath,[string]$server=([ADSI]"LDAP://rootdse").dnshostname,[switch]$verbose)
  3.     $statement=@"
  4. Set objType = getObject(""LDAP://$server/$ldapPath"")
  5. Set objProp = objType.Get("$prop")
  6. result = objProp.HighPart * (2^32) + objProp.LowPart
  7. "@
  8.     if($verbose)
  9.     {
  10.         Write-Host "+ Running Convert on $server"
  11.         Write-Host "  - Property  : $Prop"
  12.         Write-Host "  - Path      : "$ldapPath`""
  13.         Write-Host "  - Statement : $statement"
  14.     }
  15.     $vbs = new-object -com MSScriptControl.ScriptControl
  16.     $vbs.language = ‘vbscript’
  17.     $vbs.ExecuteStatement($statement)
  18.     [int64]$value = $vbs.Eval("result")
  19.     return $value
  20. }
  21.  

3) Use System.DirectoryServices.DirectorySearcher MDSN Link

  1. function Get-iADSLargeIntFromSearcher{
  2.     param([string]$LdapPath,[string]$attribute = $(throw "Attribute Required"),[string]$server,[switch]$date)
  3.     if($server){$de = [ADSI]"LDAP://$server/$LdapPath"}
  4.     else{$de = [ADSI]"LDAP://$LdapPath"}
  5.     $return = new-object system.DirectoryServices.DirectorySearcher($de)
  6.     $value = ($return.findone()).properties[$attribute.ToLower()]
  7.     if( $date)
  8.     {
  9.         [datetime]::FromFileTime([int64]::Parse($value))
  10.     }
  11.     else
  12.     {
  13.         $value
  14.     }
  15. }

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.

  1. function ConvertADSLargeInteger([object]$adsLargeInteger)
  2. {
  3.     $highPart = $adsLargeInteger.GetType().InvokeMember("HighPart",‘GetProperty’, $null, $adsLargeInteger, $null)
  4.     $lowPart  = $adsLargeInteger.GetType().InvokeMember("LowPart",‘GetProperty’, $null, $adsLargeInteger, $null)
  5.  
  6.     $bytes = [System.BitConverter]::GetBytes($highPart)
  7.     $tmp   = [System.Byte[]]@(0,0,0,0,0,0,0,0)
  8.     [System.Array]::Copy($bytes, 0, $tmp, 4, 4)
  9.     $highPart = [System.BitConverter]::ToInt64($tmp, 0)
  10.  
  11.     $bytes = [System.BitConverter]::GetBytes($lowPart)
  12.     $lowPart = [System.BitConverter]::ToUInt32($bytes, 0)
  13.  
  14.     return $lowPart + $highPart
  15. }

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
——————-

  1.  
  2. function Get-iADSLargeIntegerAttribute{
  3.     param([string]$domain)
  4.     if($domain){$rootDSE = [ADSI]"LDAP://$Domain/rootDSE"}
  5.     else{$rootDSE = [ADSI]"LDAP://rootDSE"}
  6.     $schema  = [ADSI]"LDAP://$($rootDSE.schemaNamingContext)"
  7.     $filter = "(&(objectclass=attributeschema)(attributesyntax=2.5.5.16)(omsyntax=65))"
  8.     $props = ("ldapdisplayname")
  9.     $dsearcher = new-Object System.DirectoryServices.DirectorySearcher($schema,$filter,$props)
  10.     $dsearcher.findall() | %{Write-Output "$($_.Properties['ldapdisplayname'])"}
  11. }
  12.  

UPDated highlighting Code