Posts RSS Comments RSS 117 Posts and 170 Comments till now

Scripting/SysAdmin Meme

Was called out by GPOGuy

What was your first machine?
Atari 800 or a TRS 80. Can’t remember.

What was the first real script you wrote?
Wow… The one that forced me to learn vbscript. I always knew I should, but when I got stuck in an environment with 2800 DCs in 2700+ sites. That was a lot of clicking… I had to script. I wrote a script that created the AD replication topology (scripted KCC if you like.)

What scripting languages have you used?
Used extensively: DOS batch, VBScript, PIC, PowerShell. dabbled with PHP, Perl, and Python.

What was your first professional sysadmin gig?
My first “computer” job was in high school working at a local technology repair firm. The first “sysadmin” job was for a small collection agency in Pcola Fl.

If you knew then what you know now, would have started in IT?
Absolutely. If I didnt like it I would leave. I think the most important thing about work is enjoying it.

If there is one thing you learned along the way that you would tell new sysadmins, what would it be?
Google, Google, GOOGLE! Think out of the box. Always ask why, the REAL problem isnt always clear.

What’s the most fun you’ve ever had scripting?
Decoding the UpToDateness Vector Table or my first 1000 line vbscript.

Who am I calling out?
No one… the buck stops here :)

Working with LDAP Stats Control in Powershell

What: The stats control is a LDAP control that you can pass that will tell the server to return its internal stats on a query.

Why: The stats control is a great way to see what the Domain Controller does with your filter. Like what indexes it hits, how many entries it had to visit, how much time the DC spent, and entries visited. It is very useful in creating the most efficient LDAP Query possible.

How: I Used System.DirectoryServices.Protocols.DirectoryControl to pass the LDAP control to the Server and I used System.DirectoryServices.Protocols.BERConverter along with the protocol spec here: LDAP_SERVER_GET_STATS_OID: 1.2.840.113556.1.4.970 to decode the Byte Array that was returned.

Here is what is Returned:
For 2000
- threadCount: Number of threads that were processing LDAP requests on the DC at the time the search operation was performed.
- coreTime: The time in milliseconds which core logic in the DC spent processing the request.
- callTime: The overall time in milliseconds that the DC spent processing the request.
- searchSubOperations: The number of individual operations which the DC performed in processing the request.

For 2003/2008
- threadCount: Number of threads that were processing LDAP requests on the DC at the time the search operation was performed.
- callTime: The overall time in milliseconds that the DC spent processing the request
- entriesReturned: The number of objects returned in the search result.
- entriesVisited: The number of objects that the DC considered for inclusion in the search result.
- filter: String which represents the optimized form of the search filter used by the DC to perform a search. This very well may be different than the filter that was passed.
- index: String which indicates which database indexes were used by the DC to perform the search.

For 2008 Only
- pagesReferenced: The number of database pages referenced by the DC in processing the search.
- pagesRead: The number of database pages read from disk.
- pagesPreread: The number of database pages preread from disk by the DC in processing the search.
- pagesDirtied: The number of clean database pages modified by the DC in processing the search.
- pagesRedirtied: The number of previously modified database pages that were modified by the DC in processing the search.
- logRecordCount: The number of database log records generated by the DC in processing the search.
- logRecordBytes: The size in bytes of database log records generated by the DC in processing the search.

Note:
- Must have SE_DEBUG_PRIVILEGE
- I did NOT implement SO_EXTENDED_FMT flag.
- I did NOT test 2000.
- The functions that decodes Byte Array actually return objects, but for this test I just outputed the test to mimic ADFind.exe
- Special thanks to Robin Caron, joe Richards, and Dmitri Gavrilov for help with the decoding.
- Here is GREAT Doc on the Controls (and everything else AD) [MS-ADTS]: Active Directory Technical Specification

Code:

  1. Param(
  2.         $filter = "(objectclass=*)",
  3.         $base,
  4.         $Server,
  5.         [int]$pageSize = 1000,
  6.         [string[]]$props = @("1.1"),
  7.         [switch]$StatsOnly,
  8.         [switch]$Verbose
  9.     )
  10. function CreateStatsObject2008{
  11.     Param($StatsArray)
  12.     $DecodedArray = [System.DirectoryServices.Protocols.BerConverter]::Decode("{iiiiiiiiiaiaiiiiiiiiiiiiii}",$StatsArray) # Win2008
  13.     $myStatsObject = New-Object System.Object
  14.     $myStatsObject | Add-Member -Name "ThreadCount"     -Value $DecodedArray[1]  -MemberType "NoteProperty"
  15.     $myStatsObject | Add-Member -Name "CallTime"        -Value $DecodedArray[3]  -MemberType "NoteProperty"
  16.     $myStatsObject | Add-Member -Name "EntriesReturned" -Value $DecodedArray[5]  -MemberType "NoteProperty"
  17.     $myStatsObject | Add-Member -Name "EntriesVisited"  -Value $DecodedArray[7]  -MemberType "NoteProperty"
  18.     $myStatsObject | Add-Member -Name "Filter"          -Value $DecodedArray[9]  -MemberType "NoteProperty"
  19.     $myStatsObject | Add-Member -Name "Index"           -Value $DecodedArray[11] -MemberType "NoteProperty"
  20.     $myStatsObject | Add-Member -Name "PagesReferenced" -Value $DecodedArray[13] -MemberType "NoteProperty"
  21.     $myStatsObject | Add-Member -Name "PagesRead"       -Value $DecodedArray[15] -MemberType "NoteProperty"
  22.     $myStatsObject | Add-Member -Name "PagesPreread"    -Value $DecodedArray[17] -MemberType "NoteProperty"
  23.     $myStatsObject | Add-Member -Name "PagesDirtied"    -Value $DecodedArray[19] -MemberType "NoteProperty"
  24.     $myStatsObject | Add-Member -Name "PagesRedirtied"  -Value $DecodedArray[21] -MemberType "NoteProperty"
  25.     $myStatsObject | Add-Member -Name "LogRecordCount"  -Value $DecodedArray[23] -MemberType "NoteProperty"
  26.     $myStatsObject | Add-Member -Name "LogRecordBytes"  -Value $DecodedArray[25] -MemberType "NoteProperty"
  27.     $myStatsObject
  28. }
  29. function CreateStatsObject2003{
  30.     Param($StatsArray)
  31.     $DecodedArray = [System.DirectoryServices.Protocols.BerConverter]::Decode("{iiiiiiiiiaia}",$StatsArray) # Win2003
  32.     $myStatsObject = New-Object System.Object
  33.     $myStatsObject | Add-Member -Name "ThreadCount"     -Value $DecodedArray[1]  -MemberType "NoteProperty"
  34.     $myStatsObject | Add-Member -Name "CallTime"        -Value $DecodedArray[3]  -MemberType "NoteProperty"
  35.     $myStatsObject | Add-Member -Name "EntriesReturned" -Value $DecodedArray[5]  -MemberType "NoteProperty"
  36.     $myStatsObject | Add-Member -Name "EntriesVisited"  -Value $DecodedArray[7]  -MemberType "NoteProperty"
  37.     $myStatsObject | Add-Member -Name "Filter"          -Value $DecodedArray[9]  -MemberType "NoteProperty"
  38.     $myStatsObject | Add-Member -Name "Index"           -Value $DecodedArray[11] -MemberType "NoteProperty"
  39.     $myStatsObject
  40. }
  41. function CreateStatsObject2000{
  42.     Param($StatsArray)
  43.     $DecodedArray = [System.DirectoryServices.Protocols.BerConverter]::Decode("{iiiiiiii}",$StatsArray) # Win2000
  44.     $myStatsObject = New-Object System.Object
  45.     $myStatsObject | Add-Member -Name "ThreadCount"          -Value $DecodedArray[1]  -MemberType "NoteProperty"
  46.     $myStatsObject | Add-Member -Name "CoreTime"             -Value $DecodedArray[3]  -MemberType "NoteProperty"
  47.     $myStatsObject | Add-Member -Name "CallTime"             -Value $DecodedArray[5]  -MemberType "NoteProperty"
  48.     $myStatsObject | Add-Member -Name "searchSubOperations"  -Value $DecodedArray[7]  -MemberType "NoteProperty"
  49.     $myStatsObject
  50. }
  51.  
  52. if($Verbose){$VerbosePreference = "Continue"}
  53.  
  54. Write-Verbose " - Loading System.DirectoryServices.Protocols"
  55. [VOID][System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
  56.    
  57. [int]$pageCount = 0
  58. [int]$objcount = 0
  59.  
  60. if(!$Server)
  61. {
  62.     $rootDSE = [ADSI]"LDAP://rootDSE"
  63.     $Server = $rootDSE.dnsHostName
  64.     if(!$base){$base = $rootDSE.defaultNamingContext}
  65.     switch ($rootDSE.domainControllerFunctionality)
  66.     {
  67.         0 {$expression = ‘CreateStatsObject2000 $stats’}
  68.         2 {$expression = ‘CreateStatsObject2003 $stats’}
  69.         3 {$expression = ‘CreateStatsObject2008 $stats’}
  70.     }
  71. }
  72.  
  73. Write-Verbose " - Using Server:  [$Server]"
  74. Write-Verbose " - Using Base:    [$base]"
  75. Write-Verbose " - Using Filter:  [$filter]"
  76. Write-Verbose " - Page Size:     [$PageSize]"
  77. Write-Verbose " - Returning:     [$props]"
  78. Write-Verbose " - CSV:           [$csv]"
  79. Write-Verbose " - NoHeaders:     [$noHeader]"
  80. Write-Verbose " - Count:         [$Count]"
  81. Write-Verbose " - StatsOnly:     [$StatsOnly]"
  82. Write-Verbose " - Expression:    [$expression]"
  83.  
  84. Write-Verbose " - Creating LDAP connection Object"
  85. $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($Server)  
  86. $Subtree = [System.DirectoryServices.Protocols.SearchScope]"Subtree"
  87.  
  88. Write-Verbose " + Creating SearchRequest Object"
  89. $SearchRequest = New-Object System.DirectoryServices.Protocols.SearchRequest($base,$filter,$Subtree,$props)
  90.  
  91. Write-Verbose "   - Creating System.DirectoryServices.Protocols.PageResultRequestControl Object"
  92. $PagedRequest  = New-Object System.DirectoryServices.Protocols.PageResultRequestControl($pageSize)
  93.  
  94. Write-Verbose "   - Creating System.DirectoryServices.Protocols.SearchOptionsControl Object"
  95. $SearchOptions = New-Object System.DirectoryServices.Protocols.SearchOptionsControl([System.DirectoryServices.Protocols.SearchOption]::DomainScope)
  96.  
  97. Write-Verbose "   - Creating System.DirectoryServices.Protocols.DirectoryControl Control for OID: [1.2.840.113556.1.4.970]"
  98. $oid = "1.2.840.113556.1.4.970"
  99. $StatsControl = New-Object System.DirectoryServices.Protocols.DirectoryControl($oid,$null,$false,$true)
  100.  
  101. Write-Verbose "   - Adding Controls"
  102. [void]$SearchRequest.Controls.add($pagedRequest)
  103. [void]$SearchRequest.Controls.Add($searchOptions)
  104. [void]$SearchRequest.Controls.Add($StatsControl)
  105.  
  106. $start = Get-Date
  107. while ($True)
  108. {
  109.     # Increment the pageCount by 1
  110.     $pageCount++
  111.  
  112.     # Cast the directory response into a SearchResponse object
  113.     Write-Verbose " - Cast the directory response into a SearchResponse object"
  114.     $searchResponse = $connection.SendRequest($searchRequest)
  115.  
  116.     # Display the retrieved page number and the number of directory entries in the retrieved page
  117.     Write-Verbose (" - Page:{0} Contains {1} response entries" -f $pageCount,$searchResponse.entries.count)
  118.  
  119.  
  120.     Write-Verbose " - Returning Stats for Page:$PageCount"
  121.     $stats = $searchResponse.Controls[0].GetValue()
  122.     $ResultStats = invoke-Expression $expression
  123.     if($pageCount -eq 1)
  124.     {
  125.         $StatsFilter = $ResultStats.Filter
  126.         $StatsIndex = $ResultStats.Index
  127.         Write-Verbose "   + Setting Filter to [$StatsFilter]"
  128.         Write-Verbose "   + Setting Index  to [$StatsIndex]"
  129.     }
  130.    
  131.     # If Cookie Length is 0, there are no more pages to request"
  132.     if ($searchResponse.Controls[1].Cookie.Length -eq 0)
  133.     {
  134.         if($count){$objcount}
  135.         "`nStatistics"
  136.         "================================="
  137.         "Elapsed Time: {0} (ms)" -f ((Get-Date).Subtract($start).TotalMilliseconds)
  138.         "Returned {0} entries of {1} visited - ({2})`n" -f $ResultStats.EntriesReturned,$ResultStats.EntriesVisited,($ResultStats.EntriesReturned/$ResultStats.EntriesVisited).ToString(‘p’)
  139.         "Used Filter:"
  140.         "- {0}`n" -f $StatsFilter
  141.         "Used Indices:"
  142.         "- {0}`n" -f $StatsIndex
  143.         break
  144.     }
  145.  
  146.     # Set the cookie of the pageRequest equal to the cookie of the pageResponse to request the next
  147.     # page of data in the send request and cast the directory control into a PageResultResponseControl object
  148.     Write-Verbose " - Setting Cookie on SearchResponse to the PageReQuest"
  149.     $pagedRequest.Cookie = $searchResponse.Controls[1].Cookie
  150. }

More AD CMDLets from SDMSoftware

It includes snapins to get and re-animate tombstones

Darren discusses this on his blog: Powershell Hits The Morgue

Build Lab w/ Quest AD CMDLets

Earlier I wrote a post about a script that I used to build my AD Lab Build Lab (v1 w/out Quest Tools) and I mentioned I
would post a Quest version. I had some time run it (took about 6hrs.) So without further ado:

Whats it do Again?
# Creates A TestOU OU
# Creates A TestComputers OU
# Creates A TestUsers OU
# Creates A TestGroups OU
# Creates 10K OU’s Under TestOU
## Each of the 10k OUs will have 4 Child OUs
### Each OU should have 5 users Accounts and 5 Machines Accounts
# Create 500 Group Policies.
# Link 100 policies on the 10k Base OUs
# Create 2000 Users in the TestUser OU
# Create 2000 Computers in the TestComputer OU
# Create 2K Groups

Note: Added Write-Progress for OU/User Creation

  1. # Adding Required Snapins
  2. Add-PSSnapin SDMSoftware.PowerShell.GPMC -ea 0
  3. Add-PSSnapin Quest.ActiveRoles.ADManagement -ea 0
  4.  
  5. $DomainDN = (([ADSI]"").distinguishedName[0])
  6. $DomainDNS = (([ADSI]"").distinguishedName[0]) -replace "DC=","" -replace ",","."
  7. $users = @()
  8.  
  9. # A TestOU OU
  10. $BaseOU = New-QADObject -Type OrganizationalUnit -ParentContainer $DomainDN  -Name TestOU
  11.  
  12. # A TestComputers OU
  13. $TestComps = New-QADObject -Type OrganizationalUnit -ParentContainer $DomainDN -Name TestComputers
  14.  
  15. # A TestUsers OU
  16. $TestUsers = New-QADObject -Type OrganizationalUnit -ParentContainer $DomainDN -Name TestUsers
  17.  
  18. # A TestGroups OU
  19. $TestGrps = New-QADObject -Type OrganizationalUnit -ParentContainer $DomainDN -Name TestGroups
  20.  
  21. # 10K OUs Under TestOU
  22. foreach($i in 1..10000)
  23. {
  24.     $lvl1Child = New-QADObject -Type OrganizationalUnit -ParentContainer $BaseOU.dn -Name "LvL1ChildOU$i"
  25.     Write-Progress "Creating OUs LvL1ChildOU$i" -status "Updating" -perc ($i/10000*100)
  26.     ## Each of the 10k OUs will have 4 Child OUs
  27.     foreach($x in 1..4)
  28.     {
  29.         $lvl2Child = New-QADObject -Type OrganizationalUnit -ParentContainer $lvl1Child.dn -Name "LvL2Child${i}${x}"
  30.         Write-Progress "Creating Child OUs LvL2Child${i}${x}" -status "Updating" -perc ($x/4*100) -id 1  
  31.         foreach($y in 1..5)
  32.         {
  33.             ## Each OU should have 5 users Accounts and 5 Machines Accounts
  34.             Write-Progress "Creating Child Users/Computers" -status "Updating" -perc ($y/5*100) -id 2
  35.             New-QADUser -ParentContainer $lvl2Child.dn -Name "usr${i}${x}${y}" -SamAccountName "usr${i}${x}${y}" -UserPrincipalName "usr${i}${x}${y}@$DomainDNS" -UserPass "!P@ssw0rd22!" | Out-Null
  36.             New-QADObject -ParentContainer $lvl2Child.dn -name "srv${i}${x}${y}" -objectAttributes @{"sAMAccountName"="srv${i}${x}${y}`$"} -type "Computer" | out-Null
  37.         }
  38.     }
  39. }
  40.  
  41. # Create 500 Group Policies.
  42. 1..500 | %{New-SDMgpo "TestGPO$_"}
  43.  
  44. # Link 100 policies on the 10k Base OUs
  45. 1..100 | %{Add-SDMgpLink -name "TestGPO$_" -scope "OU=LvL1ChildOU$i,$($BaseOU.DN)"}
  46.  
  47. # Create 2000 Users in the TestUser OU
  48. 1..2000 | %{New-QADUser -ParentContainer $TestUsers.dn -Name "Testusr$_" -SamAccountName "Testusr$_" -UserPrincipalName "Testusr$($_)@$DomainDNS" -UserPass "!P@ssw0rd22!"}
  49.  
  50. # Create 2000 Computers in the TestComputer OU
  51. 1..2000 | %{New-QADObject -ParentContainer $TestComps.dn -name "TestComp$($_)" -objectAttributes @{"sAMAccountName"="TestComp$($_)`$"}}
  52.  
  53. # Create 2K Groups
  54. 1..2000 | %{New-QADGroup -ParentContainer $TestGrps.dn -name "TestGrp$_" -sAMAccountName "TestGrp$_"}

Effective Powershell Series (Keith Hill)

Keith Hill has an incredible series call Effective Powershell. If you’re new to Powershell or heck… even if you have been using it for awhile. Please check it out.

Series Starts Here with Item 1
Full Series Here (start from bottom up)

Powershell and MFCom (Citrix takes more steps)

Vishal Ganeriwala is hosting a webinar on the 29th of April on Powershell and MFCom. This is just another example of Citrix actively embracing Powershell.

http://community.citrix.com/blogs/citrite/vishalg/2008/04/21/Free+Managing+Citrix+Servers+via+PowerShell+and+MFCOM+webinar

It will cover some basic Powershell for those who do not have much XP with Powershell.
- What is an Object
- Properties
- Methods
- New-Object (-ComObject)

It will also cover how to create a Citrix Farm Object in Powershell and some of the interactive nature of Powershell
- Getting Farm Properties
- Getting Applications
- Getting Servers
- Getting Sessions

Example
- Get Current Sessions Client Resolution

Get-CitrixSessionUser.ps1 (Citrix Top 10)

One of the script I was asked to convert was a script to output all the Users and the Client IP.

This is what I came up with

Name: Get-CitrixSessionUser.ps1
Purpose: List ClientAddress and UserName for each Session

  1. # Get-CitrixSessionUser.ps1
  2. # Brandon Shell [MVP]
  3. # www.bsonposh.com
  4. # List ClientAddress and UserName for each Session
  5.  
  6. $farm = new-Object -com "MetaframeCOM.MetaframeFarm"
  7. $farm.Initialize(1)
  8. $farm.Sessions | Format-Table UserName,ClientAddress

After I did this… I stopped and actually thought about what I was doing. What is the point? What would someone use this for? It seemed to me that a simpler script that was more flexible would not only meet the original goal, but expand it significantly. It comes back to the object nature of Powershell. I needed to stop thinking strings and start thinking objects. Using the following script not only accomplishes the same goal to get UserName and ClientAddress, but also expands it to add the following:

AAName
AAType
AccessSessionGuid
Applications
AppliedPolicy
AppName
AverageLatency
BandwidthCap
BytesRcvdPostExpansion
BytesRcvdPreExpansion
BytesSentPostCompression
BytesSentPreCompression
ClientAddress
ClientAddrFamily
ClientBuffers
ClientBuild
ClientCacheDisk
ClientCacheLowMem
ClientCacheTiny
ClientCacheXms
ClientColorDepth
ClientDimBitmapMin
ClientDimCacheSize
ClientDimVersion
ClientDirectory
ClientEncryption
ClientHardwareID
ClientHRes
ClientID
ClientLicense
ClientModemName
ClientModules
ClientName
ClientProductID
ClientProductIDValue
ClientVRes
DeviceID
ICABufLen
InputSpeed
LastLatency
LatencyDeviation
OutputSpeed
Processes
ProtocolType
ServerBuffers
ServerName
SessionID
SessionName
SessionState
SmartAccessFilters
UserName
VIPAddress
VirtualChannels

  1. # Get-CitrixSession.ps1
  2. # Brandon Shell [MVP]
  3. # www.bsonposh.com
  4. # Gets all the Sessions for a Farm
  5.  
  6. $farm = new-Object -com "MetaframeCOM.MetaframeFarm"
  7. $farm.Initialize(1)
  8. $farm.Sessions

Interpreting > Parsing (who knew!)

I found out some interesting information yesterday. I thought I would share it.

Back Story:
A question was asked on how to include $env:ComputerName to a string. I quickly piped up “HA! That is easy just wrap @() around the variable and it will work.” While this indeed works and is what I have been doing for the last couple of years… There was a better way!

It turns out that Jim Truher was watching this particular thread discussion and quickly informed me that this was “quite inefficient” and I should use ${} instead. So of course, I had to know why. Luckily Bruce Payette was also watching this thread and explained the following.

I hope he doesn’t mind if I quote him.

“The big difference is whether the parser gets involved or not. In the ${foo} case, the interpreter simply extracts the string, looks up the variable and inserts the result. In the $($var) case, the string is extracted, parsed and then executed which is more overhead.”

More Info (provided by Keith Hill):
$() is used within strings to evaluate statements (potentially multiple statements) and the results are converted into a string (Using OFS.)

${} is simply used to prevent the interpreter from “interpreting” the name of the variable instead it is treated as the literal name of the variable

Moral of the Story?
From now on I am using ${} when no evaluating is needed.

Jims Blog
http://jtruher.spaces.live.com/

Keith Hill has an awesome Series called Effective Powershell
http://keithhill.spaces.live.com/

Bruce hangs out on the Powershell Forum
http://blogs.msdn.com/PowerShell/

Bruce also wrote Windows Powershell in Action
http://www.manning.com/payette/

p.s. I tested this with this code

  1. # with ${}
  2. ( Measure-Command {1..10000 | %{ "${env:path}" } } ).TotalMilliseconds
  3. # vs with $()
  4. ( Measure-Command {1..10000 | %{ "$($env:path)" } } ).TotalMilliseconds

SpecOps and Group Policies… What a match!

Special Operations Software has created an Incredible marriage of Powershell and Group Policy. Please take some time to watch these Demos. AWESOME!

Specops Command done by Darren Mar-Elia:
http://www.specopssoft.com/powershell/specopscommand-sdm.wmv

Specops Deploy done by Derek Melber:
http://www.specopssoft.com/products/specopsdeploy/specops_deploy.wmv

Consortio Services Podcast

I had the chance to spend some time with the Consortia folks last night discussing Powershell and its place for the administrator in the next couple of years. Please stop by and listen to the Podcast.

http://www.cstechcast.com/home.aspx?Episode=8

More Info:
Consortio Services was founded by 3 technology experts who have over 35 years of combined expertise in nearly all facets of Information Technology. They have gained real world knowledge in such diverse areas as Networking Systems infrastructure, Telephony support, Operating System maintenance, Database Architecture, Design, and Administration, and Software design and support.

Eric Beehler - Network Technologies Consultant
Eric Johnson - Database Technologies Consultant
Joshua Jones - Operating Systems Consultant

Visit the website here
http://www.consortioservices.com/index.shtml

Next »