Zoho Banner September 2011

Archive for the 'PowerShell' Category

What’s in a name?

 

I’ve been working with the Windows Server 2008 R2 Active Directory cmdlets quite a bit recently.  If you’ve come from a background of using ADFIND, the cmdlets take a bit of getting used, but for me it’s definitely been worth the time spent.  One thing that frustrates me a little is that the names used by the cmdlets for AD attributes tend to vary greatly.  Not only is there inconsistency between cmdlets that stem from different modules and snap-ins, but also between the cmdlets and the traditional UI tools.  The table below shows some examples:

 

AD Users and Computers

ldapDisplayName

AD cmdlet property name

Exchange 2010 cmdlet property name

Telephone Number

telephoneNumber

OfficePhone

Phone

E-mail

mail

EmailAddress

WindowsEmailAddress

P.O. Box

postofficeBox

POBox

PostOfficeBox

 

I guess the Microsoft Product Groups try to protect the long-suffering customer from the obscurity of some of the attribute LDAP display names by giving them friendly names.  This approach would work well if there were universal consistency, but it not it just becomes confusing.  My preference is always to use the ldapDisplayName values whenever possible – and certainly always with the AD cmdlets, e.g.

Get-ADUser –LDAPFilter “(samaccountname=bobm)”

If in doubt however, refer to the object model:

get-help about_ActiveDirectory_ObjectModel

Tony

Powershell script to find objects using objectGUID value

The objectGUID attribute is a little tricky to work with, especially if you want to use it as part of an LDAP filter.  This is because the value in stored within the directory as an octet string - essentially an array of one-byte characters.  This syntax is not especially user-friendly, which is why it is typically displayed (by tools such as LDP.EXE and the AD Powershell cmdlets) in a registry string format, e.g. “af97d4c7-5f17-4ce2-9245-687d410b4b20″Another way of displaying the value is using a hex string format, e.g. “C7D497AF175FE24C9245687D410B4B20″.  If you’re into migrations using Quest Migration Manager for AD, the hex string representation is used for the matching attribute value (using either extenstionAttribute15 or adminDisplayName).  As you can see, the two representations are similar, but have a slightly different ordering of the bytes.

You’d think that you would be able to use one of these two string representations of the objectGUID as part of an LDAP search filter, wouldn’t you?  Well, no, that would be too helpful.  Instead, you need a slightly modified version of the hex string, i.e. “\C7\D4\97\AF\17\5F\E2\4C\92\45\68\7D\41\0B\4B\20″.   The search filter syntax would look like this “(objectGUID=\C7\D4\97\AF\17\5F\E2\4C\92\45\68\7D\41\0B\4B\20)”

The script below accepts either of the two string formats as input and prompts you for the naming context in which to search for the object represented by the objectGUID attribute.

#########################################################
#
# Name: Get-ADObjectFromGUID.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 24/08/2010
# Comment: PowerShell script to search for an AD object
# using 'objectGUID' as the LDAP search filter
#
#########################################################

# Import the AD module
ipmo ActiveDirectory

# Get the objectGUID value
$Input1 = @'
    Please enter the objectGUID value in one
    of the two following formats:

    Registry string format, e.g.
        bb67681f-0ac1-471a-bf3d-f7f4c4cb1290
    or
    Hex string format, e.g.
        455E54E9D58B0F4B8F389E4982791D40

'@

$strGUID = Read-Host $Input1

# Present Menu
[string] $menu = @'
    Choose the AD partition to search within
    1. Current Domain
    2. Configuration
    3. Schema
    4. Other

'@

$a = Read-Host $menu
switch ($a)
{
    1 { $SearchBase = (Get-ADRootDSE).defaultNamingContext }
    2 { $SearchBase = (Get-ADRootDSE).configurationNamingContext }
    3 { $SearchBase = (Get-ADRootDSE).schemaNamingContext }
    4 { $SearchBase = Read-Host "Enter the search base: " }

}

if ($strGUID.length -eq 36)
{
    # We have a string in registry format and need to convert it to Hex string
    $strHex = -join (([guid]$strGUID).tobytearray() | %{$_.tostring("X").padleft(2,"0")})
}
elseif ($strGUID.length -eq 32)
{
    # We have a string in Hex format - no need to modify
    $strHex = $strGUID
}
else
{
    # Unrecognised string format
    Write-host "Unrecognised string format - remove any leading or trailing spaces and try again"
    Break
}

# We need to modify the Hex string to allow it to be used as a filter
$strSearch = $strHex -replace '(..)','\$1'

# Go ahead and search for the object
Get-ADObject -LDAPFilter "(objectGUID=$strSearch)" -SearchBase $SearchBase -Properties * | fl

# End

You can download the script here: get-adobjectfromguid.zip

Teched 2011 New Zealand - Powershell files

As promised, here are the Powershell data files from my Teched NZ 2011 demos.  I’ve also included the FSMO role mover script.

te_2011.zip

Let me know if you have any questions. Enjoy!

How to find duplicate sAMAccountNames between two forests

When preparing for a migration of AD objects from one forest to another it is useful to know if any of the names are going to conflict.  There are, as you probably know, a number of different naming attributes in AD, but the one most likely to cause problems in the event of a conflict is sAMAccountName.  This is because sAMAccountName is used for domain logon (assuming UPN is not used).  If you know which names conflict between source and target you can plan changes before you get into the migration itself.

I’ve written a couple of small scripts to detect sAMAccountName conflicts.  I’ve used two scripts instead of one as there is not always trust connectivity between the two forests.  The first script (SourceUsersToCSV) is run in the source domain and basically just dumps all the user sAMaccountName attribute values to a file.  The second script (DupeCheckFromCSV) is run in the target domains and reads the exported file information line-by-line and checks to see if there are any conflicts in the target domain.  If a conflict is found the sAMAccountName is written to a file.

Enjoy!

#########################################################
#
# Name: SourceUsersToCSV.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 14/07/2011
# Comment: PowerShell script to export AD user info
# to CSV to support search for duplicate users
#
#
#########################################################

### --- Varible Definitions ---

$DOMAIN = "source.com"
$EXPFILE = "C:\util\CSV\domain_export.csv"
$sourceOU = "OU=MyUsers,DC=source,DC=com"
$filter = "(&(objectClass=user)(!iscriticalsystemobject=TRUE))"

### --- Main ---

# Export Source AD User info to file

if(@(get-module `
| where-object {$_.Name -eq "ActiveDirectory"} ).count -eq 0) {import-module ActiveDirectory}

$objSourceDC = Get-ADDomainController -Discover -DomainName $DOMAIN
$sourceDC = [string]$objSourceDC.HostName
$UserInfo = '' | Select 'UsrsAMAccountName'
$AllUsers = @()
$MyUsers = Get-ADUser -LDAPFilter $filter -Server $sourceDC -SearchBase $sourceOU
foreach($User in $MyUsers) {
    $UserInfo.'UsrsAMAccountName' = $User.sAMAccountname
    $AllUsers += $UserInfo | Select 'UsrsAMAccountName'
}
$AllUsers | Export-Csv $EXPFILE -NoTypeInformation
#########################################################
#
# Name: DupeCheckfromCSV.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 14/07/2011
# Comment: PowerShell script to import AD User info
# from CSV to check for duplicates
#
#########################################################

### Set Global variables

$domain = "target.com"
$impfile = "C:\util\CSV\Domain_Export.csv"
$EXPFILE = "C:\util\CSV\Duplicate_Users.csv"
$dc = Get-ADDomainController -Discover -DomainName $domain
$targetdc = [string]$dc.HostName
$arrSrcSAMs=@()
$arrTarSAMs = @()
$arrDupeUsers = @()

### Load the CSV file and extract the source domain unique User names

$colsrcUsers = import-csv $impfile #| select UsrSAMAccountName

foreach ($srcUser in $colsrcUsers)
	{
		$srcSAM = $srcUser.UsrSAMAccountName
		$arrsrcSAMs += $srcSAM
	}    

# Check for empty file
if ($colsrcUsers.Count -eq 0)
    {
        write-host "No Users found in CSV import file!"
        break
    }

### Enumerate Users in the local (target) domain 

$coltarUsers = Get-ADUser -Filter '*' -Server $targetDC -Properties samaccountname

foreach ($tarUser in $coltarUsers)
	{
		$tarSAM = [string]$tarUser.samaccountname
		$arrTarSAMs += $tarSAM
	}    

### Find Users to Add or Modify

foreach ($tarUser in $arrTarSAMs)
	{
		if ($arrSrcSAMs -contains $tarUser)
		{
			write-host "Duplicate User found for " $tarUser
			$arrDupeUsers += $tarUser
		}
		else
		{
			write-host "No Duplicate found for " $tarUser
		}
	}

### Add new Users to target and apply the membership

$arrDupeUsers | out-file $EXPFILE

How to clear group membership with Powershell

Something I often recommend to my customers is keep the membership of the Enterprise Admins and Schema Admins groups empty and only populate them (temporarily) when required.  The privileges assigned to these groups are obviously high and removing the members reduces the potential for costly mistakes and/or compromise. 

Here’s a quick Powershell snippet that will perform the removal:

$grps = "Enterprise Admins", "Schema Admins"
foreach ($grp in $grps) {
Get-ADGroupMember -Identity $grp `
| %{Remove-ADGroupMember -Identity $grp -Members $_ -Confirm:$false}
}

This is something that you could consider running as a scheduled task to ensure the memberships are kept clear.

How to find the renamed domain Built-In Administrator account with Powershell

Many organisations choose to rename the Built-in Administrator account for the domain for security reasons.  Whether or not renaming the account provides any real protection is the matter of some debate.  What is clear is that any hacker worth his or her salt is not going to be fooled by the rename, because the account has a well known security identifer:

SID: S-1-5-21domain-500

I was working on something the other day and needed to find the Built-in Administrator account using Powershell.  It wasn’t quite as straightforward as I thought it would be.  Anyway, here’s what I came up with:

$BA = (Get-ADDomain).domainsid
$BA = $BA.ToString() + "-500"
Get-ADUser -Identity $BA

As you can see it basically involves grabbing the domain SID, adding on the well-known identifier “-500″ and then searching for the account based on the concatenanted string. 

I can’t help thinking there must be an easier method, so if you have one please post a comment here.

Powershell script to change NIC label based on location

Here’s a little WMI script I threw together the other day to help someone out.  The requirement was to set the display name (label) of a network adapter based on its hardware location.

Let’s say we have a NIC with the display name “Local Area Connection” and you want to change it to “MyNIC”, but only if it has a specific hardware location (e.g. PCI bus 3, device 0, function 0).  Of course, you can do this manually by nipping into the network connection properties (ncpa.cpl), but what if you have tens or hundreds of servers that you want to do this for?

nic_details_before.jpg

nic_configure.jpg

#########################################################
#
# Name: SetNetConnectionID.ps1
# Author: Tony Murray
# Version: 1.0
# Date: 20/04/2011
# Comment: PowerShell script to
# change device display name for network adapter based
# on hardware location
#
#########################################################
$newname = "MyNIC"
$location = "PCI bus 3, device 0, function 0"
$drvinfo = (Get-WMIObject Win32_PnPSignedDriver `
| ?{$_.Location -eq $location}).DeviceID
$nic = Get-WMIObject Win32_NetworkAdapter `
| ?{$_.PNPDeviceID -eq $drvinfo}
$oldname = $nic.NetConnectionID
write-host "Network Adapter name before change: " $oldname
$nic.NetConnectionID = $newname
$nic.Put()

After running the script, you should see that the display name has changed.

nic_details_after.jpg

My script only changes the NIC display name for the computer on which it is running, but you could easily change this to run against multiple machines.

GAL Sync with Powershell

If you’re looking for a Global Address List synchronisation solution for Exchange that simply uses Powershell, look no further than this excellent script from fellow MVP Carol Wapshere.

http://www.wapshere.com/missmiis/a-galsync-powershell-script

The script doesn’t leverage the DirSync control (and hence doesn’t use deltas), which means that it isn’t perhaps as efficient as some of the full-blown solutions out there, but it has the beauty of simplicity!  Another advantage is that it doesn’t require any expensive infrastructure components - unlike most solutions that need at least one dedicated server and a database.

It also works with a variety of Exchange versions!

The script is likely to be most useful for SMEs during migration scenarios. Larger organisations or those looking for something long-term are more likely to invest in a more comprehensive solution such as FIM, SimpleSync or Quest ActiveRoles QuickConnect.

I encourage you to check it out.

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

Next Page »