Posts RSS Comments RSS 253 Posts and 411 Comments till now

Blog Archives

AD Replication Metadata (when did that change?)

There was a discussion on the NG about determining when a user was disabled. The initial request was to determine this based on whenChanged, but I thought that could be invalid as you can easily change an account after it was disabled. I can not think of a way to be sure, but the best way I can think of is to use the replication metadata on the attribute userAccountControl (the second bit is what determines if its disabled or not.) While it is possible to change the useraccountcontrol after a user is disabled it is unlikely.

More info for UserAccountControl bits
http://support.microsoft.com/kb/305144

Of course the next question was how do you check the Replication Metadata for an attribute on and AD object?

Enter Get-ADObjectREplicationMetadata.ps1

This uses

System.DirectoryServices.ActiveDirectory.DirectoryContext
– http://msdn2.microsoft.com/en-us/library/system.directoryservices.activedirectory.directorycontext.aspx
System.DirectoryServices.ActiveDirectory.DomainController
– http://msdn2.microsoft.com/en-gb/library/system.directoryservices.activedirectory.domaincontroller.aspx

# Get-ADObjectREplicationMetadata.ps1
# Brandon Shell (www.bsonposh.com)
# Purpose: Get attribute(s) Replication Metadata from a Domain controller.
Param($Domain,$objectDN,$property)
# Sets Context to Domain for System.DirectoryServices.ActiveDirectory.DomainController
$context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$domain)
# .NET Class that returns a Domain Controller for Specified Context
$dc = [System.DirectoryServices.ActiveDirectory.DomainController]::findOne($context)
# GetReplicationMetadata returns metadate from the DC for the DN specified.
$meta = $dc.GetReplicationMetadata($objectDN)
if($property){$meta | %{$_.$Property}}else{$meta}

This will return either all the metadata or just the metadata for a specific attribute. I should note that if you do not specify an attribute it returns all of them. You should expect to parse these as each attribute has a child object with the data in it.

All Attributes. The value can be found by .PropertyName

PS# .\Get-ADObjectMetaData.ps1 ‘my.lab.domain’ ‘CN=TestUser,DC=my,dc=lab,dc=domain’

Name                           Value
—-                           —–
countrycode                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
cn                             System.DirectoryServices.ActiveDirectory.AttributeMetadata
mail                           System.DirectoryServices.ActiveDirectory.AttributeMetadata
scriptpath                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
ntsecuritydescriptor           System.DirectoryServices.ActiveDirectory.AttributeMetadata
accountexpires                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
displayname                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
profilepath                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
primarygroupid                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
unicodepwd                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
objectclass                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
objectcategory                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
instancetype                   System.DirectoryServices.ActiveDirectory.AttributeMetadata
homedrive                      System.DirectoryServices.ActiveDirectory.AttributeMetadata
samaccounttype                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
homedirectory                  System.DirectoryServices.ActiveDirectory.AttributeMetadata
whencreated                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
useraccountcontrol             System.DirectoryServices.ActiveDirectory.AttributeMetadata
msmqsigncertificates           System.DirectoryServices.ActiveDirectory.AttributeMetadata
dbcspwd                        System.DirectoryServices.ActiveDirectory.AttributeMetadata
title                          System.DirectoryServices.ActiveDirectory.AttributeMetadata
samaccountname                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
supplementalcredentials        System.DirectoryServices.ActiveDirectory.AttributeMetadata
userparameters                 System.DirectoryServices.ActiveDirectory.AttributeMetadata
givenname                      System.DirectoryServices.ActiveDirectory.AttributeMetadata
description                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
lmpwdhistory                   System.DirectoryServices.ActiveDirectory.AttributeMetadata
pwdlastset                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
msnpallowdialin                System.DirectoryServices.ActiveDirectory.AttributeMetadata
codepage                       System.DirectoryServices.ActiveDirectory.AttributeMetadata
name                           System.DirectoryServices.ActiveDirectory.AttributeMetadata
ntpwdhistory                   System.DirectoryServices.ActiveDirectory.AttributeMetadata
userprincipalname              System.DirectoryServices.ActiveDirectory.AttributeMetadata
admincount                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
objectsid                      System.DirectoryServices.ActiveDirectory.AttributeMetadata
sn                             System.DirectoryServices.ActiveDirectory.AttributeMetadata
msmqdigests                    System.DirectoryServices.ActiveDirectory.AttributeMetadata
logonhours                     System.DirectoryServices.ActiveDirectory.AttributeMetadata
lastlogontimestamp             System.DirectoryServices.ActiveDirectory.AttributeMetadata

Here is a specific Attribute

PS# .\Get-ADObjectMetaData.ps1 ‘my.lab.domain’ ‘CN=TestUser,DC=my,dc=lab,dc=domain’ ‘useraccountcontrol’

Name                        : userAccountControl
Version                     : 8
LastOriginatingChangeTime   : 9/15/2005 1:45:32 PM
LastOriginatingInvocationId : eeaeb6f9-8422-dddd-as34-04d7bd779285
OriginatingChangeUsn        : 47264036
LocalChangeUsn              : 49555172
OriginatingServer           : dc.my.lab.domain

Error Handling: Part3 – The Power of Set-PSDebug

From a scripting perspective this CMDLet is one of the greatest tools you could possibly have access to. The guys over at the Powershell Podcast also did a quick overview in Latest Podcast at http://powerscripting.net

Lets start with an overview of the CMDLet. A good portion of these will be covered in the help file (PS> get-help Set-PSDebug -full) So I highly recommed reviewing that documentation as well.
-trace
    0 – turn off tracing
    1 – trace script lines as they are executed
    2 – trace basically everything
-step: Will prompt you for action
    [Y] Yes (Default): Will Process the line and prompt again at the next.
    [A] Yes to All: Will process all lines, Usually used after you Caught what you were looking for.
    [N] No: Will STOP processing on all lines, effectively terminating the script.
    [L] No to All: Will STOP processing on all lines, effectively terminating the script.
    [S] Suspend: This option is awesome. This will open another runspace where you can inspect the
         environment from the shell. Basically, if you want to figure out what the state of a variable or object
         is at a given time. You can just Suspend and get the information or change it.
         Type exit goes to the next prompt.

Set-PSDebug -step
Continue with this operation?
   1+ C:\tools\Testme.ps1 hello
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
DEBUG:    1+ C:\tools\Testme.ps1 hello

Continue with this operation?
   2+ $myhi = $hi
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
DEBUG:    2+ $myhi = $hi

Continue with this operation?
   3+ Write-Host "$Myhi was passed" -fore Green
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
DEBUG:    3+ Write-Host "$Myhi was passed" -fore Green
hello was passed

    Here are some gotcha’s for Stepping
    – “[A] Yes to All” effectively turns off stepping
    – “[N] No” and “[L] No to All” both do the same thing… Kill the processing.
    – -step Automatically sets debug trace to level 1

-off: Turns off Debugging

-strict: Variable must be assigned a value before being referenced (Kind of like Option Explict in Vbscript.)

Set-PSDebug -strict
$myvar                     # This line will throw an error
$myvar = "Hi There"
$myvar                     # This line will output "Hi There"

What does the Debug output look like?

Here is the script we will be debugging

Param($hi)
function foo{
   Param($iFoo)
   Return "$iFoo was Passed"
}
$myvar = foo $hi
Write-Host $myvar

Here is the script with Set-PSDebug -Trace 1

PS: Set-PSDebug -Trace 1
PS: C:\tools\Testme.ps1 Hi
DEBUG:    1+ C:\tools\Testme.ps1 Hi
DEBUG:    2+ function foo{
DEBUG:    6+ $myvar = foo $hi
DEBUG:    4+    Return "$iFoo was Passed"
DEBUG:    7+ Write-Host $myvar
Hi was Passed

Things to Notice
– Line numbers are referenced in the order they were called
– The Debug info is Prefaced with “DEBUG:”
– Stepping is not used by default

Here is the script with Set-PSDebug -Trace 2

PS: Set-PSDebug -Trace 2
PS: C:\tools\Testme.ps1 Hi
DEBUG:    1+ C:\tools\Testme.ps1 Hi
DEBUG:     ! CALL script ‘Testme.ps1’
DEBUG:    2+ function foo{
DEBUG:    6+ $myvar = foo $hi
DEBUG:     ! CALL function ‘foo’  (defined in file ‘C:\tools\Testme.ps1’)
DEBUG:    4+    Return "$iFoo was Passed"
DEBUG:     ! SET $myvar = ‘Hi was Passed’.
DEBUG:    7+ Write-Host $myvar
Hi was Passed

Things to Notice
– You Now see calls to scripts, functions and variable assignments
– Calls to Scripts, Functions, and Setting Variable are prefaced with !
– Calls to function tell you where they came from

Summary:
I hope this post does justice to Set-PSDebug. It is extremely useful and powerful for writing scripts. I truly hope you take some time to play with this CMDLet and see where it takes you. It has done wonders for me.

Bonus:
Wanna see how the evironment effects a CMDLet or even just a little peak under the hood? Use Set-PSDebug -trace 2

69# Set-PSDebug -trace 2
DEBUG:    1+ Set-PSDebug -trace 2
70# Get-ChildItem c:\fes
DEBUG:    1+ Get-ChildItem c:\fes
DEBUG:    2+                                     if ($ErrorView -ne "CategoryView") {
DEBUG:    3+                                        $myinv = $_.InvocationInfo
DEBUG:     ! SET $myinv = ‘System.Management.Automation.InvocationInfo’.
DEBUG:    4+                                        switch -regex ($myinv.MyCommand.CommandType)
DEBUG:   24+                                                if ($myinv.MyCommand.Name)
DEBUG:   26+                                                    $myinv.MyCommand.Name + " : "; break;
DEBUG:   26+                                                    $myinv.MyCommand.Name + " : "; break;
DEBUG:    2+                                     if ($_.InvocationInfo) {
DEBUG:    3+                                         $posmsg = $_.InvocationInfo.PositionMessage
DEBUG:     ! SET $posmsg =
At line:1 char:14
+ Get-ChildItem  <<<< c:\fes’
.
DEBUG:    7+                                     if ($ErrorView -eq "CategoryView") {
DEBUG:   11+                                         $_.Exception.Message + $posmsg
Get-ChildItem : Cannot find path ‘C:\fes’ because it does not exist.
At line:1 char:14
+ Get-ChildItem  &lt;&lt;&lt;&lt; c:\fes

The Beginning and the End (the process to)

In my powershell journeys I have found a huge quantity of glorious abilities wrapped up in this truly POWER shell. One of these superpowers is the concept of filter/function processing. While I do not consider myself a guru in any stretch I have done a good bit of work with functions and think I have a sound grasp of this ability.

I haven’t used filters that much so I will stick with what I know… functions. Oh… glorious functions! These have been the life blood of my powershell experience. I came from a VBScript back ground and have always been super anal (or lazy) about code reuse. I would always try to black box my code to be able to reuse later. While this is a good practice IMHO… it does tend to get lost sometimes… This is where I believe Powershell functions come in.

There a ton of things you can do with functions and I don’t have time to discuss them all, so I am going to focus on my favorite…. Begin, Process, and End oh my!

Lets start with the basic layout of a function:

function foo{
   write-host "Hello World"
}

Ok… nothing spectacular there, but what if I want to be more specific to whom I say hello

function foo{
   param([string]$name)
   Write-Host "Hello $name"
}

This is nice… but still… nothing big going on. I mean seriously… what kinda scripting language can’t a take parameters.

function foo{
   Param([string]$name)
   Begin{
       # Only gets process at the Beginning
       # Normally include Variable creation and functions
       Write-Host "Starting"
   }
   Process{
      # Gets process for each object in the pipe (if ones exist)
      if($_){Write-Host "Hello $_"}
   }
   End{
      # Always get processed once at the end
      if($name){Write-Host "Hello $name"}
   }
}

Now… as trivial as this looks this function is truly amazing. It not only has the ability to take a parameter, but it can also take a pipe (pipe is explained below.)

Like I said, functions of lots of abilities but this is my favorite. Thanks to Jeffrey and the Team…. GREAT IDEA!!!

===============================================
pipe: A pipe is when you get the results of one command and pipe them to another… Kinda like you did in DOS or BASH, but instead of pass text… in powershell you pass objects.

« Prev