Posts RSS Comments RSS 253 Posts and 411 Comments till now

Serverless binding with ADSI

joe and I were discussing this yesturday and he blog about it here So I used Serverless Binding with ADSI (or .NET), now what DC am I talking to?

On a side note… I think this is bad form. IMO you should be very deliberate with your code even with discovery. I would use rootDSE to do the discovery then stick with that server.

Some AD Functions for DCs and name conversion.

Below are some functions I have written or used recently that I don’t believe I shared before

Get-DC: Gets a DirectoryServices.ActiveDirectory.DomainController object by Name or Domain. If nothing is passed it gets a DC from the current domain

Get-DCConnectionObject: Gets the connection objects for the given DC. Default is all DCs

ConvertTo-Sid: Converts Name to SID.

ConvertTo-Name: Converts Sid to Name.

################################################################################
function Get-DC
{
    Param($Name,$Domain)
   
    if($Name)
    {
        $Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$Name)
        [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($Context)
    }
    if($Domain)
    {
        $Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$Domain)
        [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($Context)
    }
    if(!$Name -and !$Domain)
    {
        $DCName = ([adsi]"LDAP://rootDSE").dnsHostname.ToString()
        $Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$DCName)
        [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($Context)
    }
}
################################################################################
function Get-DCConnectionObject
{
    Param($name = ".*")
    $Myforest = [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
    $MyDCs = $Myforest.Domains | foreach-object{$_.DomainControllers} | ?{$_.name -match $name}
    $MyDCs | %{$_.InboundConnections}
}
################################################################################
function ConvertTo-Sid($UserName,$domain = $env:Computername)
{
   $ID = New-Object System.Security.Principal.NTAccount($domain,$UserName)
   $SID = $ID.Translate([System.Security.Principal.SecurityIdentifier])
   $SID.Value
}
################################################################################
function ConvertTo-Name($sid)
{
   $ID = New-Object System.Security.Principal.SecurityIdentifier($sid)
   $User = $ID.Translate( [System.Security.Principal.NTAccount])
   $User.Value
}

blog: Why use LDAP filters (Powershell)

A common problem when dealing with Active Directory is the end user trying to parse the results themselves.

Let take this example
  1. $selector = New-Object DirectoryServices.DirectorySearcher
  2. $selector.SearchRoot = [ADSI]""
  3. $selector.pagesize = 1000
  4. $adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"}
  5. foreach ($person in $adobj) {
  6. $date120DaysAgo = [DateTime]::Now.AddDays(-120).ToFileTime()
  7. $LL1 = $person.properties.lastlogontimestamp
  8. if(($LL1 -le $date120DaysAgo) -and ($person.GetDirectoryEntry().psbase.invokeget('AccountDisabled'))){$person}
  9. }

Instead of doing the parsing on results side... we should let the server do the work. How do we do that?

With LDAP filters. Here is an example.
  1. $filter = "(&(objectcategory=user)(userAccountControl:1.2.840.113556.1.4.803:=2)(lastlogontimestamp>=$date))"
  2. $ds = New-Object DirectoryServices.DirectorySearcher([ADSI]"",$filter)
  3. $ds.PageSize = 1000
  4. $users = $ds.FindAll()
  5. $users

Or with Quest tools... even easier!
  1. $date = (Get-Date).AddDays(-120).ToFileTime()
  2. $filter = "(lastlogontimestamp>=$date)"
  3. Get-QADUser -LdapFilter $filter -disabled

I think you will find with an LDAP filter you can save a TON of time.

Here is the output of measure-command for the two examples above (this was a very small sample.)

Without Filter
--------------
Days : 0
Hours : 0
Minutes : 0
Seconds : 3
Milliseconds : 477
Ticks : 34776670

With Filter
-----------
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 34
Ticks : 340740

If you were to do this on a large AD the difference in time would be HUGE! Here is an example with 600K users...

With Filter
------------
Days : 0
Hours : 0
Minutes : 0
Seconds : 17
Milliseconds : 353
Ticks : 173535605

I can't post one with out filter... because it has been hours and it is still not done :)

Testing AD LDS (ADAM) replication with Powershell

Earlier this month I had a discussion with Laura (of AD Cookbook fame) regarding ADLDS and how to test convergence. After a few minutes I remembered I had a AD convergence script I wrote a while back found HERE. With a little tweaking (specifically discoverability) we converted it to test ADLDS as well. Below you will fine the result.

Parameters
Server: The ADLDS/ADAM server that hosts the application partition you want to test
DN: The distinguished name of the application partition you want to test (will try to discover)
Port: Port ADLDS/ADAM list on (Default 389)
Table [switch]: A switch that outputs an object with the results.

Note: Please feel free to provide any feedback you have regarding this. I do not use ADLDS or ADAM so other than my test environment I really cannot play with this.

The Code
Test-LDSReplication.ps1


 

Param($Server = $Env:ComputerName,
      $DN,
      $Port = "389",
      [switch]$table
      )

function Ping-Server {
   Param([string]$srv)
   $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’ and Timeout=1000"
   if($pingresult.statuscode -eq 0) {$true} else {$false}
}

$DirectoryServer = "{0}:{1}" -f $Server,$Port

$Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$DirectoryServer)
$ADAM = [System.DirectoryServices.ActiveDirectory.AdamInstance]::GetAdamInstance($context)

if(!$DN)
{
    $AppPartition = $ADAM.ConfigurationSet | %{$_.ApplicationPartitions} | Select-Object -first 1
    $DN = $AppPartition.Name
    $dclist = $AppPartition.DirectoryServers | ?{$_.HostName -notmatch $Server}
}
else
{
    $dclist = $ADAM.ConfigurationSet.AdamInstances | ?{($_.Partitions -contains $DN) -and ($_.HostName -notmatch $Server)}
}

if($table)
{
    $DCTable = @()
    $myobj = "" | select Name,Time
    $myobj.Name = ("$Server [SOURCE]").ToUpper()
    $myobj.Time = 0.00
    $DCTable += $myobj
}

$timestamp = [datetime]::Now.ToFileTime().ToString()
Write-Host "`n  Modifying wwwHomePage Attribute on Object [$DN] on [$DirectoryServer] with value [$timestamp]"
$object = ([ADSI]"LDAP://$DirectoryServer/$DN")
$object.wWWHomePage = $timeStamp
$object.SetInfo()
$objectDN = $object.distinguishedname
Write-Host "  Object [$objectdn] Modified! `n"

$start = Get-Date

$i = 0

Write-Host "  Found [$($dclist.count)] LDS replicas"
$cont = $true

While($cont)
{
    $i++
    $oldpos = $host.UI.RawUI.CursorPosition
    Write-Host "  =========== Check $i ===========" -fore white
    start-Sleep 1
    $replicated = $true
    foreach($dc in $dclist)
    {
        if($server -match $dc.HostName){continue}
        if(ping-server $dc.HostName)
        {
            $DCServer = "{0}:{1}" -f $dc.HostName,$dc.LdapPort
            $object = [ADSI]"LDAP://$DCServer/$dn"
            if($object.wwwHomePage -eq $timeStamp)
            {
                Write-Host "  – $DCServer Has Object Description [$dn]" (" "*5) -fore Green
                if($table -and !($dctable | ?{$_.Name -match $dc.HostName}))
                {
                    $myobj = "" | Select-Object Name,Time
                    $myobj.Name = $dc.HostName.ToUpper()
                    $myobj.Time = ("{0:n2}" -f ((Get-Date)$start).TotalSeconds)
                    $dctable += $myobj
                }
            }
            else{Write-Host "  ! $($dc.HostName.ToUpper()) Missing Object [$dn]" -fore Red;$replicated  = $false}
        }
        else
        {
            Write-Host "  ! $($dc.HostName.ToUpper()) Failed PING" -fore Red
            if($table -and !($dctable | ?{$_.Name -match $dc}))
            {
                $myobj = "" | Select-Object Name,Time
                $myobj.Name = $dc.HostName.ToUpper()
                $myobj.Time = "N/A"
                $dctable += $myobj
            }
        }
    }
    if($replicated){$cont = $false}else{$host.UI.RawUI.CursorPosition = $oldpos}
}

$end = Get-Date
$duration = "{0:n2}" -f ($end.Subtract($start).TotalSeconds)
Write-Host "`n    Took $duration Seconds `n" -fore Yellow

if($table){$dctable | Sort-Object Time}

Getting users group membership (tokengroups)

Around the same time as I wrote my Linked-Value Attribute script I also came up with this little gem. It also uses a constructed attribute provided with Windows 2003 called “tokengroups.” (Did I mention this includes recursive groups?)

Effectively it gets the attribute which returns an array of SIDs (in byte array form) for each group. I then use a function I posted about eariler called ConvertTo-Name to convert that BYTE array into a friendly group name we humans like.

Parameters:

  • – Account: Can be User samAccountName or DN of the user
  • – Verbose: Enables verbose output

More Info:
You may notice the GetInfoEx call I make on the user object. This is because tokengroups is not an actual attribute and does not get “populated” until you specifically request it. The GetInfoEx does exactly that.

Links:
MSDN: GetInfoEx
MSDN: tokenGroups

Get-TokenGroups.ps1

Param($Account,[switch]$Verbose)

if($verbose){$verbosepreference="Continue"}

Write-Host
Write-Verbose " – Account: $Account"
Write-Verbose " – Verbose: $Verbose"

function ConvertTo-Name($sid,[switch]$FromByte) {
    if($FromByte)
    {
        $ID = New-Object System.Security.Principal.SecurityIdentifier($sid,0)
    }
    else
    {
        $ID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    }
    if($ID)
    {
        $User = $ID.Translate([System.Security.Principal.NTAccount])
        $User.Value
    }
}
function GetDNfromName{
    Param($name)
    $root = [ADSI]""
    $filter = "(sAMAccountName=$name)"
    $props = @("distinguishedName")
    $Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
    $Searcher.FindOne().properties.distinguishedname
}

if($Account -notmatch "CN=(.*),((OU|DC)=\w*)*")
{
    Write-Verbose " + Getting User DN for [$Account]"
    $Account = GetDNfromName $Account
    Write-Verbose "   – GetDNfromName returned [$Account]"
}

Write-Verbose " – Getting User Object"
$UserAccount = [ADSI]"LDAP://$Account"

Write-Verbose " – Calling GetInfoEx"
$UserAccount.GetInfoEx(@("tokengroups"),0)

Write-Verbose " – Getting tokengroups"
$groups = $UserAccount.Get("tokengroups")

Write-Verbose " + Processing Groups"
foreach($group in $groups)
{
    $GroupName = ConvertTo-Name $group -FromByte
    Write-Verbose "   – Found group [$GroupName]"
    $GroupName
}

Write-Host

Constructed Properties and LVR (Linked-Value Replication)

There was an interesting question that came up on the news group that discussed getting Active Directory replication metadata and while the question really wasn’t directly a Powershell question I found it intriguing. I knew I had the answer to the question back in my brain, but I couldn’t retrieve it.

Here is the Question:

“I’m working on a script where I need to compare the last time the member
attribute of a distribution group was modified (not the AD group object
itself) with the time stamp on a file (I’m exporting distribution group
memberships to a file, but only ones that have changed). The problem
I’m running into is that some Active Directory distribution groups
aren’t returning the member attribute when I look at the replication
meta data.

Any one have any thoughts on why some distribution groups return the
member attribute when I run GetReplicationMetadata and some don’t? This
returns all kinds of other attributes and their metadata, but naturally
not the one I’m interested in (it does some times, on some groups, but
not all). I could run my script by the actual AD object WhenChanged
attribute, but I’ll be processing a large number of lists and I want it
to run as fast as possible and since other attributes can change on a
group object, I don’t want to have to export a 8,000 member group if the
displayName changes, for example (I’m only interested in the member
attribute).
As always, any insight is appreciated.”

I thought about this for a bit and also consulted some AD friends and we determined the issue was LVR (Link Value Replication.) This was introduced in Windows 2003 (specifically 2003 Native Mode.)

Basically, LVR changed the unit of replication for some attributes to be the actual values. Prior to LVR if you changed group membership the entire attribute member would have to replicate. With LVR, just the link you added for user replicates.

As one can imagine this changed the Metadata and therefore GetReplicationMetadata() didnt get retrieve the data for you. Where does this leave us?

There were also a few constructed attributes that were added with 2003. One of which is called “msds-replvaluemetadata.” This attribute provided the metadata for these links in wonderful XML format. You will find code using this XML below.

Some Useful Links regarding LVR and Constructed Attributes:

Below is the script that resulted from the investigation


 

Get-GroupMemberMetadata.ps1

Param($GroupName)

$GroupMembers = @()

$root = [ADSI]""
$filter = "(&(objectclass=group)(name=$GroupName))"
$props = @("msDS-ReplValueMetaData")
$Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
foreach($Group in $Searcher.findall())
{
    foreach($childMember in $Group.Properties."msds-replvaluemetadata")
    {
        $GroupMembers += [xml+site:msdn.microsoft.com”>XML]$ChildMember
    }
}

foreach($Member in $GroupMembers)
{
    $Member.ChildNodes
}

Setting the WriteProperty on Members (ManagedBy Check box)

This has come up three times in the last week which triggers my auto blog response 🙂

Below is a function called New-ADAce. This function creates an Access Control Entry that can be applied to an AD Object (in this case the member property of an AD object.)

Basically what the code below does is:

  • Gets the ID object of the Manager
  • Creates ACE that gives the Manager Write access to the member property
  • Gets the Object to be managed
  • Gets the existing ACL
  • Addes the ACE to the ACL
  • Sets the ManagedBy Property to the DN of the Manager
  • Commits the changes
Param(
   $myGuid = "bf9679c0-0de6-11d0-a285-00aa003049e2", #GUID for the Members property
   $DN = "cn=jloser,cn=users,dc=corp,dc=lab",
   $domain = $env:UserDomain,
   $manager = "jmanager",
   $MangedByDN = ""cn=jmanager,cn=users,dc=corp,dc=lab""
)

function New-ADACE {
   Param([System.Security.Principal.IdentityReference]$identity,
   [System.DirectoryServices.ActiveDirectoryRights]$adRights,
   [System.Security.AccessControl.AccessControlType]$type,
   $Guid)

   $help = @"
   $identity
      System.Security.Principal.IdentityReference
      http://msdn.microsoft.com/en-us/library/system.security.principal.ntaccount.aspx
   $adRights
      System.DirectoryServices.ActiveDirectoryRights
      http://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectoryrights.aspx
   $type
      System.Security.AccessControl.AccessControlType
      http://msdn.microsoft.com/en-us/library/w4ds5h86(VS.80).aspx
   $Guid
      Object Type of the property
      The schema GUID of the object to which the access rule applies.
"
@
   $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity,$adRights,$type,$guid)
   $ACE
}

# Some example code on how to use the New-ADACE function

# Create ACE to add to object
$ID = New-Object System.Security.Principal.NTAccount($domain,$manager)
$newAce = New-ADACE $ID "WriteProperty" "Allow" $myGuid

# Get Object
$ADObject = [ADSI]"LDAP://$DN"

# Set Access Entry on Object
$ADObject.psbase.ObjectSecurity.SetAccessRule($newAce)

# Set the manageBy property
$ADObject.Put("managedBy",$MangedByDN)

# Commit changes to the backend
$ADObject.psbase.commitchanges()

Functions for Active Directory Permissions

Below are some of the function I have written to work with Access Control List in Active Directory.

Here is the list and what they do
Get-ADACL: Gets the Access Control List of an AD Object
Set-ADACL: Set the Access Control List of an AD Object
New-ADACE: Creates a Access Control Entry to add to an AD Object
ConvertTo-Sid: Converts a UserName to a SID
ConvertTo-Name: Converts a SID to the UserName
Get-Self: Gets current Security Identity


function Get-ADACL {
    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])
    }
}
function Set-ADACL {
    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
}
function New-ADACE {
    Param([System.Security.Principal.IdentityReference]$identity,
        [System.DirectoryServices.ActiveDirectoryRights]$adRights,
        [System.Security.AccessControl.AccessControlType]$type,
        $Guid)
   
    $help = @"
    $identity
        System.Security.Principal.IdentityReference
        http://msdn.microsoft.com/en-us/library/system.security.principal.ntaccount.aspx
   
    $adRights
        System.DirectoryServices.ActiveDirectoryRights
        http://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectoryrights.aspx
   
    $type
        System.Security.AccessControl.AccessControlType
        http://msdn.microsoft.com/en-us/library/w4ds5h86(VS.80).aspx
   
    $Guid
        Object Type of the property
        The schema GUID of the object to which the access rule applies.
"
@
    $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity,$adRights,$type,$guid)
    $ACE
}
function ConvertTo-Sid($UserName,$domain = $env:Computername) {
   $ID = New-Object System.Security.Principal.NTAccount($domain,$UserName)
   $SID = $ID.Translate([System.Security.Principal.SecurityIdentifier])
   $SID.Value
}
function ConvertTo-Name($sid) {
   $ID = New-Object System.Security.Principal.SecurityIdentifier($sid)
   $User = $ID.Translate( [System.Security.Principal.NTAccount])
   $User.Value
}
function Get-Self{
   ([Security.Principal.WindowsIdentity]::GetCurrent())
}

Setting lDAPAdminLimits via Powershell

I was having a conversation with a friend the other day and he brought up a question about updating the AD property lDAPAdminLimits.

The Problem
The property is stored as an array of string values (at least as far as Powershell is concerned.) The initial reaction was do try something like this $queryPolicies.lDAPAdminLimits.MaxNotificationPerConn = 30, but this assumed that MaxNotficationPerConn was a property of lDAPAdminLimits and not the actual value (or at least part of the value.)

The Solution
Use the ADSI method PutEX to modify the value. PutEx uses ADS_PROPERTY_OPERATION_ENUM to make selective changes to an existing property. In the script below, we add the new value using the Update operation and then use the Delete operation to remove the old value.

Some Examples of Use

D:\Scripts\Set-ldapAdminPolicy.ps1 MaxNotificationPerConn 45
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxQueryDuration 360
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxPageSize 500
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxPoolThreads 8

Here is a link on how to view/set via NTDSUtil.exe
How to view and set LDAP policy in Active Directory by using Ntdsutil.exe

The Code

Param($policy=$(throw ‘$policy is required’),$count=30)
$rootDSE = [ADSI]"LDAP://rootDSE"
$config = $rootDSE.configurationNamingContext
$queryPolicies = [adsi]"LDAP://CN=Default Query Policy,CN=Query-Policies,cn=Directory Service,cn=Windows NT,CN=Services,$config"
$oldvalue = $queryPolicies.lDAPAdminLimits | ?{$_ -match $policy}
$queryPolicies.PutEx(3,"lDAPAdminLimits",@("$policy=$count"))
$queryPolicies.Setinfo()
$queryPolicies.PutEx(4,"lDAPAdminLimits",@("$oldvalue"))
$queryPolicies.Setinfo()

Bulk Imports using CSV (cross post from TurboChargeAD.org)

As some of you may know I am doing some guest blogging over at TurboChargeAD.org and below is a link to the second such article.

Here is the Link: Bulking Importing User from CSV file using Quest cmdlets

Here is the code

#Import-ADUser.ps1
#requires -pssnapin Quest.ActiveRoles.ADManagement
Param($file,$OU,[switch]$whatif,[switch]$verbose)

if($verbose){$VerbosePreference = "Continue"}

if(!(test-path $file))
{
   throw " File NOT found!"
   return
}

if(!$ou)
{
    # Setting the OU to the Users container
    $ou = "CN=Users,{0}" -f ([ADSI]"").distinguishedName[0]
}

Write-Verbose " Using File: $file"
Write-Verbose " Using OU: $ou"

foreach($user in (Import-Csv $file))
{
    $props = @{}
    # Getting a list property names.
    $propNames = $user | Get-Member -MemberType properties | %{$_.name}
    # Foreach of the property names add an entry in the hash table with
    # the key being the property name and value being the value from the object
    foreach($prop in $propNames)
    {
        # This removes quotes if they exist
    $value = $user.$prop -replace "’|`"",""
        $props += @{$prop=$value}
    }
    # Create User using the displayname as the CN
    $MyUser = new-qaduser -name $user.displayName `
                          -ObjectAttributes $props `
                          -parent $OU `
                          -whatif:$whatif `
                          -verbose:$verbose
    $MyUser
}

Next »