Zoho Banner September 2011

Archive for the 'Active Directory' Category

Bulk create sample AD users from CSV file using Powershell

Many moons ago I provided a CSV file containing 200 sample AD user accounts together with instructions on how to use CSVDE to import the accounts into AD. I often use these sample accounts in lab environments and for presentations. I thought it would be fun to write a small powershell script to read the same CSV input file and create the accounts using the AD cmdlets from Windows Server 2008 R2.

You can download the CSV file here.

And here’s the Powershell script….

#########################################################
#
# Name: BulkCreate200UsersFromCSV.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 29/12/2010
# Comment: PowerShell 2.0 script to
# bulk create users from csv file
#
#########################################################

# Function to test existence of AD object
function Test-XADObject() {
   [CmdletBinding(ConfirmImpact="Low")]
   Param (
      [Parameter(Mandatory=$true,
                 Position=0,
                 ValueFromPipeline=$true,
                 HelpMessage="Identity of the AD object to verify if exists or not."
                )]
      [Object] $Identity
   )
   trap [Exception] {
      return $false
   }
   $auxObject = Get-ADObject -Identity $Identity
   return $true
}

# Import the Active Directory Powershell Module
Import-Module ActiveDirectory -ErrorAction SilentlyContinue

# Specify the target OU for new users
$targetOU = "OU=Standard Users,OU=_North,DC=North,DC=com"

# Find the current domain info
$domdns = (Get-ADDomain).dnsroot # for UPN generation

# Specify the folder and CSV file to use
$impfile = "C:\util\CSV\200Users.csv"

# Check if the target OU is valid
$validOU = Test-XADObject $targetOU
If (!$validOU)
{
 write-host "Error: Specified OU for new users does not exist - exiting...."
 exit
} 

# Set the password for all new users
$password = read-host "Enter password" -assecurestring

# Parse the import file and action each line
$users = Import-CSV $impFile
foreach ($user in $users)
{
$samname = $user.samaccountname
$dplname = $user.displayname
$givname = $user.givenname
$surname = $user.sn
$upname = "$samname" + "@" +"$domdns"
New-ADUser –Name $dplname –SamAccountName $samname –DisplayName $dplname `
-givenname $givname -surname $surname -userprincipalname $upname `
-Path $targetou –Enabled $true –ChangePasswordAtLogon $true `
-AccountPassword $password
}

#All done

You can download the script here.

GAL Sync with Quest Quick Connect Express for Active Directory

Quest has just released a freeware product called Quick Connect Express for Active Directory. If you’re looking for something that fills the gap left by the (now pensionable) Microsoft Identity Integration Feature Pack, then this may well be it.  I’ve spent the last day looking at the product’s capabilities for running Exchange 2010 Global Address List synchronisation and I have to say I’ve very impressed.  Aside from the price tag ($0.00 suits the pocket of most), the best thing about QQCE is that it’s really easy to set up and configure.

At the time of writing the download link points to the wrong version of Quick Connect.  I only worked this out when the installer prompted me for a connection to the ActiveRoles Server Administration Service (something not used with the Express version).  I’m sure Quest will sort this out soon, but in the meantime, you can obtain the correct version by registering with the Quest Support web site and downloading the software and associated documentation from there.  You should end up with the following two files:

  • Quest_QuickConnectSyncEngineStandaloneModex64_470.msi (this is the 64-bit version – a 32-bit version is also available)
  • QuickConnectStandaloneMode_4.7_AdminGuide.pdf

Optionally, you can also download the Quick Connect Powershell provider:

  • Quest_QuickConnectManagementShellStandaloneModex64_470.msi

I chose to install the software on a Windows Server 2008 R2 Standard Edition machine together with SQL Server 2008 R2.  QQCE uses SQL databases to store the synchronisation data, but SQL Server doesn’t need to be on the same machine.

If I have time I’ll work on a step-by-step guide for GAL Sync using QQCE, but in the meantime here are some of the configuration details I used in my lab. ***Update Jan 2011 - step-by-step guide now available***

My first forest (ad.contoso.com) runs Windows Server 2008 R2 functional level and has Exchange 2010 SP1 RU2.  The second forest (ad.fabrikam.com) has the same versions.  I joined the server running QQCE to the CONTOSOE domain, but it could equally have been joined to the FABRIKAM domain.  I then configured GAL Sync in the direction CONTOSO –> FABRIKAM.  In other words, I had users in CONTOSO that I wanted to appears as Contacts in the FABRIKAM Global Address List.  Of course it is also possible to perform two-way GAL sync with the tool – I just didn’t take it that far.

Once you have completed the installation, the first thing to do is select one of the two domains as the “Managed Domain” for QQCE.  For the purposes of GAL sync the selection is arbitrary.  You then configure the second domain as a “Connected System”.

qqc_connections1

You then need to set up the workflow.  I chose to configure three separate workflow steps for the GAL Sync (Provisioning, Update and Deprovisioning).  This ensures that any creation, modification or deletion of mailbox-enabled users in CONTOSO are reflected in FABRIKAM.

The provisioning aspect of the workflow requires the most work.  The tool can easily be configured to provision Contact objects in the target, but a custom post-sync Powershell script is required to ensure the contacts appear in the GAL.

qqc_provisoning1

My script (which you can download here) invokes a remote Powershell session against a FABRIKAM Exchange server and uses the Get-Contact and Set-Contact cmdlets to ensure the attributes required for GAL visibility are stamped on the Contact objects.

qqc_provisoning_script

When configuring the Source information, I specified the OU containing the mailbox-enabled User objects and identified them using the homeMDB attribute.  If the homeMDB attribute is present on a User object you can assume it is mailbox-enabled.

qqc_provisoning2

The Target window in the configuration wizard allows you to specify what object type to create (Contact in my case).  You also specify the rule(s) for generating the object name. I chose the source User object’s Display Name attribute to generate the name (cn) for the corresponding Contact object.

 qqc_provisoning3

Finally, you specify which attributes on the source object should be populated on the target object during provisioning.  My choices are fairly obvious, but note in particular that I used the mail attribute from the source to create the Contact object’s targetAddress attribute.  The targetAddress attribute is important for Contacts as it is the one Exchange uses for routing purposes.

qqc_provisoning4

The Update and Deprovisioning steps are much simpler to configure, so I won’t show them here. 

Once the workflow setup is compete you can configure them to run according to a schedule that you specify.  Once per day is probably sufficient in most cases.

The remaining task is to create a mapping rule for the User->Contact relationship.  This is required to allow the Update and Deprovisioning workflow steps to match the correct target object based on changes or deletions in the source domain.

qqc_workflow1

And that’s it really.  You can pretty much have the whole thing up and running in an hour or less!

Much kudos to Quest for pushing this out as a free tool.  Of course GAL Synchronisation is not the sole purpose of the tool, but I suspect it’ll be the major drawcard for many organisations given its usefulness in migration and coexistence scenarios.  The major alternatives for GAL Sync with Exchange 2010 are ILM/FIM and SimplSync, both of which cost money and, in the case of FIM at least, require a great deal more configuration effort.

Tony

Exchange Extension Attributes – A Cautionary Tale

I had always been under the impression that the custom attributes introduced by the Exchange schema extensions were handy for all sorts of things.  Earlier this week I discovered that there are hidden dangers when using them for anything other than Exchange-related purposes.

When you prepare the schema for Exchange you will see, amongst others, 15 new attributes named extensionattribute1 to extensionAttribute15 (common name ms-Exch-Extension-Attribute-n).  These attributes are exposed via the UI tools, which makes them user-friendly to administrators and support staff.

These custom attributes have a Unicode string syntax, so you can store just about any information you might need in them.  They also form part of attribute set replicated via the Global Catalog and are indexed in AD.  All of these things make the attributes useful as generic “bucket” to store user or group-related information without the hassle of developing your own schema extensions.

extensionAttribute1

As an example, Quest Migration Manager defaults to using two extension attributes (14 and 15) when it detects that the required Exchange schema extensions are in place.  QMM uses these as service attributes to support synchronisation and migration. 

Sounds perfect right?  Well I thought so until I discovered the other day the attribute values are blown away when mailbox-enabling or mailbox-disabling a user account.  For example, when you disable a mailbox it disconnects it from the parent AD user object and at the same time strips the user object of all its Exchange-related attribute value, including the extension attributes.  The Disable-Mailbox cmdlet does this silently and without warning - like a thief in the night!

It would be nice if Exchange could just leave these attributes alone.  The fact that they are subject to being summarily cleared means that (for me at least) they shouldn’t be used to store anything other than Exchange-related information.

Teched 2010 New Zealand - Powershell command dictionary file

After my Teched break-out session yesterday I was asked if I could share the PS command dictionary file that I used to support my demos.  Here it is (right-click Save Target As):

CommandDictionary.psd1

For those of you that weren’t there the PS commands mostly relate to working with the AD Recycle Bin feature of Windows Server 2008 R2 Active Directory.

Give me a shout if you have any follow-up questions.

Powershell script to bulk convert linked mailboxes

As part of a recent domain migration, I had to convert a number of linked mailboxes to standard “user” mailboxes.  The well known method for converting linked mailboxes is to disconnect the mailbox, change the type and then reconnect.  This is fine you just have a few linked mailboxes to deal with, but not if you have a whole lot. 

I found a great script written by Georg Hinterhofer that seemed to be just what I needed.  When I tried the script it failed because the values for $AclContainingAEA show as being in the form <domain>\<user>.  Normally, this would be ok, but in my case the user objects involved had been migrated with SIDHistory.  This mean that the $AclContainingAEA values were interpreted by the script as being from the target domain, when in fact behind the scenes the values corresponded to the SID from the source domain.  It meant I had to do a little trickery to get the script to work for my scenario.  My update to the script is here: convertlinkedmailbox.txt

Georg wrote the script for Exchange 2007,  but it works equally well for Exchange 2010.

Powershell script to add group members based on sIDHistory

In a migration scenario it is sometimes useful to have a security and/or distribution Active Directory group in the target domain where the membership is comprised of migrated user objects.  Here’s a Powershell 2.0 script that I put together that populates the membership of a group based on a specific sIDHistory value.  It can be run as a one-off after the migration or can be invoked via a scheduled task to keep up to date during a migration.

The script also creates a new event log source and then writes the logging information to the application event log on the machine from which it is run.  This is not essential to the script, so scrub it out if you want to. 

You can download a copy of the script here: sidhistorybasedgroupmembership.txt

#########################################################
#
# Name: SIDHistoryBasedGroupMembership.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 11/07/2010
# Comment: PowerShell 2.0 script to
# populate group membership based on sIDHistory values
#
#########################################################  

#Import the Active Directory Powershell Module
Import-Module ActiveDirectory -ErrorAction SilentlyContinue   

#Create a new Event log source for the script (only needs to be run once)
New-EventLog -logName Application -Source "Legacy Users Group Management" `
-ErrorAction SilentlyContinue   

$SearchBase = "OU=User Objects,DC=fabrikam,DC=local"
$OUArr = Get-ADUser -LDAPFilter "(samaccounttype=805306368)" `
-SearchBase $SearchBase -SearchScope SubTree   

# Now we need the domain security identifier or at least a portion of it
$DomSID = "S-1-5-21-1584567894-2535104369-4141123456"   

$Group = "Legacy Users"
$MbrArr = get-adgroupmember -identity $Group   

# Loop through the Users found beneach the OU tree
# and check to see if the user is already
# a member of the group. If so, do nothing.
# If not, then add the user as a member.
Foreach ($User in $OUArr)
{
    $object = [ADSI]"LDAP://$User"
    $objectsidh = $object.sIDHistory.value
    If (!$objectsidh)
    {
        # write-host "sIDHistory is blank"
    }
    Else
    {
        $objectsidh = $Object.getex(“sidhistory”)
        trap
            {
            #write-host "Error: $_"
            continue
            }
        foreach($sid in $objectSidh)
        {
            $sidh = new-object System.Security.Principal.SecurityIdentifier $sid,0
            if ($sidh -Match $DomSID)
            {
                if ($MbrArr -Match $User.distinguishedName)
                {
                    #The user is already member - do nothing
                }
                else
                {
                    # We need to add the user as a member
                    write-eventlog -logname Application `
                    -source "Legacy Users Group Management" `
                    -eventID 3001 -entrytype Information -message "$User added to $Group"
                    Add-ADGroupMember -Identity $Group -Members $User
                }
            }
            else
            {
                # No match with sidHistory - do nothing
            }
        }
    }
}

64-bit Version of Acctinfo2.dll

Some time ago I blogged about the Acctinfo2.dll tool and how unfortunate it was that a 64-bit version was not available.  Well, the good news is that you can now download a 64-bit version from here:

 Acctinfo2_64bit.zip

I have tested the DLL on both Windows Server 2008 and Windows Server 2008 R2 and it seems to work well.  However, please note this version is completely unsupported!  Download at use entirely at your own risk.

Tony

Powershell Script to Pre-Seed Computer Objects in AD

Sometimes it’s useful to pre-create computer objects in the correct OU before joining them to the domain.  This way, you know that they will immediately pick up whatever Group Policies have been assigned to the OU.  Of course, you can create the computer objects in AD manually using Active Directory Users and Computers (dsa.msc) or the new Active Directory Administrative Center (dsac).  However, if you’ve got more than a few computer objects to create it might be helpful to have a script.  Here’s a Powershell 1.0 sample:

##########################################################
# Name: PreSeedComputerObjects.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 12/04/2010
# Comment: PowerShell 1.0 script to
# pre-create AD Computer objects from csv file
#
#########################################################     

# Set the target OU where the computer objects will be created
$ComputerOU  = [ADSI]“LDAP://OU=Workstations,DC=contoso,DC=com“     

# Specify the folder and CSV file to use
$folder = "C:\util\csv"
Set-Location $folder     

$csv = Import-Csv “import.csv”     

# Parse the CSV file line by line
foreach($line in $csv) {
# Assign variables to each attribute
$ComputerName = $line.ComputerName
$samname = $ComputerName + "$"
$Computer = $ComputerOU.create(“Computer”,”cn=$ComputerName”)     

# Populate the minimum set of attributes needed for computer objects
$Computer.put(“sAMAccountName”,$samname)
$Computer.put(“userAccountControl”,4128)
# Commit the changes
write-host "Adding $ComputerName to target OU"
$Computer.setinfo()
# Capture any errors (e.g. object already exists) and move on
        trap
            {
            write-host "Error: $_"
            continue
            }
}
#End

The format of the CSV file is simply as follows:

ComputerName
<netbios_name_of_computer>

e.g.
ComputerName
wkstn001
wkstn002
wkstn003

The only other point of interest is that we need to define the sAMAccountName and the userAccountControl attributes in the script.  The sAMAccountName is simply the NetBIOS name of the machine with a “$” suffix.  It is also important to specify an appropriate value for userAccountControl - in this case a decimal value of 4128 which corresponds to 0×1020 (hex) or (PASSWD_NOTREQD | WORKSTATION_TRUST_ACCOUNT ).

As always, please let me know if you can think of ways to improve the script.  Yes, that includes you Brandon!

Note: When copying the script from the web site, replace the double-quotes before you try it. Wordpress does some funky format changes!

Powershell OU Shadow Script

It is sometimes useful to have the ability to populate group membership based on the OU in which the prospective members are located.  A good example of where this might be useful is with Fine-Grained Password Policy (FGPP) in Windows Server 2008 AD (and later).  FGPP does not have the ability to use an OU as its scope of management - you are limited to assigning the policy to user or group objects.

The script below shadows a specified OU and populates a group’s membership based on the contents of the OU.  It is intended to be invoked by the Windows Task Scheduler (taskschd.msc).

 Note that it requires Powershell 2.0 and uses the Active Directory module.

#########################################################
#
# Name: OUShadow.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 26/03/2010
# Comment: PowerShell 2.0 script to set the members of
# a group based on the OU they live in
#
#########################################################  

#Import the Active Directory Powershell Module  

Import-Module ActiveDirectory -ErrorAction SilentlyContinue  

#Set Variables
$Group = "OU Shadow"
$SearchBase = "OU=User Accounts,DC=Contoso,DC=Com"
$MbrArr = get-adgroupmember -identity $Group
$OUArr = Get-ADUser -LDAPFilter "(samaccounttype=805306368)" -SearchBase $SearchBase  

# Loop through the Users found in the OU
# and check to see if the user is already
# a member of the group.
Foreach ($User in $OUArr)
{
if ($MbrArr -Match $User.distinguishedName)
    {
    # The user is already member - do nothing
    }
else
    {
    # We need to add the user as a member
    Add-ADGroupMember -Identity $Group -Members $User
    }
}  

# Loop through the group membership and remove
# any users that are not in the OU
Foreach ($Mbr in $MbrArr)
{
if ($OUArr -Match $Mbr.distinguishedName)
    {
    # Found user in OU - do nothing
    }
else
    {
    # We need to remove the user as a member
    Remove-ADGroupMember -Identity $Group -Members $Mbr -confirm:$false
    }
}
# End

Bulk Create Active Directory Contact Objects

If you’re creating contact objects in Active Directory the Exchange cmdlets New-MailContact, Set-MailContact and Set-Contact are usually sufficient.  On the other hand I haven’t found a way using these cmdlets to set all the attributes that I might need.  For example, the “description” attribute doesn’t appear to feature anywhere.

Things have obviously changed with the AD Powershell Provider and associated cmdlets in Windows Server 2008 R2, but here’s a script to bulk create contacts  from CSV file if you’re still using Powershell 1.0.

The format of the requried CSV file looks like this:

givenName,sn,displayName,mail,description
Bob,Smith,”Bob Smith”,bob.smith@gmail.com,”External Supplier”
Sue,Jones,”Sue Jones”,sue.jones@hotmail.com,”Hadware Sales”
Graeme,Turner,”Graeme Turner”,graeme.turner@yahoo.com,partner

#########################################################
#
# Name: BulkCreateContacts.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 13/12/2009
# Comment: PowerShell 1.0 script to
# bulk create AD Contact objects from csv file
#
#########################################################

# Set the target OU where the contacts will be created
$ContactOU=[ADSI]“LDAP://ou=Contacts,dc=mycompany,dc=com“

# Find our current working directory
$working = $(Get-Location)

# Specify the folder and CSV file to use
$folder = “C:\util\Powershell\CSV”
Set-Location $folder
$csv = Import-Csv “contacts.csv”

# Parse the CSV file line by line
foreach($line in $csv) {

# Assign variables to each attribute
$givenName = $line.givenName
$sn = $line.sn
$displayName = $line.displayName
$mail = $line.mail
$description = $line.description
$targetAddress = $line.mail

# Go ahead and create the contact object
$Contact = $ContactOU.create(“Contact”,”cn=$displayName”)
# Set the attributes on the contact object
$Contact.Put(“givenName”,$givenName)
$Contact.put(“sn”,$sn)
$Contact.put(“displayName”,$displayName)
$Contact.put(“mail”,$mail)
$Contact.put(“description”,$description)
$Contact.put(”targetAddress”,$targetAddress)
# Commit the changes
$Contact.setinfo()
# Mail-enable the contact (if you need to)
Enable-MailContact -Identity $displayName -ExternalEmailAddress $targetAddress
}
# Go back to the original working directory
Set-Location $working

« Previous PageNext Page »