Archive for the 'Active Directory' Category

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

How to revert the forest functional level in Windows Server 2008 R2

Windows Server 2008 R2 introduces the ability to revert to an earlier forest or domain functional level.  Previous versions of Active Directory did not have this ability and raising the forest or domain function level was, effectively, a one-way operation.  I say “effectively” because it is possible to revert, but only through highly disruptive recovery operations (full forest recovery in the case of a forest functional level upgrade).

I’ve actually not experienced or even heard of anyone having serious problems following a functional level upgrade, but that’s not really the point.  Have you ever tried to get approval from a Change Manager when your proposed backout plan involves a full forest outage of 12 hours or more?  In providing a mechanism for reversing a functional level upgrade, Microsoft has responded to customer demand for peace of mind, rather than a fix for a known problem.

This article shows how to raise the forest functional level and then revert it using Powershell commands.  The screenshots shown below are from running the commands in Powershell ISE.

On a Domain Controller, open a Powershell prompt and (assuming you don’t already have it) import the Active Directory Powershell module.

Import-module ActiveDirectory

Show the current Domain Functional Level

get-addomain | format-list domainmode

get-domain-mode-1.jpg

Show the current Forest Functional Level

get-adforest | format-list forestmode

get-forest-mode-1.jpg

Set the Forest Functional Level to Windows 2008 R2 (Forest Functional Level 4)

set-adforestmode -identity ad.contoso.com -forestmode windows2008R2forest

Show the current Forest Functional Level

get-adforest | format-list forestmode

get-forest-mode-2.jpg

Revert the Forest Functional Level to Windows 2008 (Forest Functional Level 3)

set-adforestmode -identity ad.contoso.com -forestmode windows2008forest

Show the current Forest Functional Level

get-adforest | format-list forestmode

You should see that the functional level has reverted successfully.

Note that after raising the functional level certain subsequent operations prevent you from reverting.  One of these operations is enabling the AD Recycle Bin, as shown below.

Enable-ADOptionalFeature 'Recycle Bin Feature' -Scope ForestOrConfigurationSet -Target ad.contoso.com

After enabling the AD Recycle Bin you will receive the error below if you try to revert the functional level.

attempt-to-revert-after-enabling-recycle-bin.jpg

Another limitation is that you can currently only revert from Windows Server 2008 R2 functional level to Windows Server 2008 functional level.  In other words, the feature has not be retro-fitted to support earlier functional levels.

As you can see, raising and reverting the functional level is fairly straightforward operation once you have the appropriate Powershell commands.  It should give you (and your Change Manager)  a degree of comfort when planning your upgrade. :-)

Error when installing AD Management Gateway Service

I went to install the AD Management Gateway Service (KB968934) on a Domain Controller running Windows Server 2008 SP2 and I received the error shown below.

KB968934_Error

Installer encountered an error: 0x80070422.  The service cannot be started, either because it is disabled or because it has not enabled devices associated with it.

The problem was that the Windows Update service had been set to Disabled by Group Policy on the DC.  After enabling the service (temporarily) the msu package installed ok.

Kind of obvious really, but it had me guessing for a while.

Domain and Forest Functional Levels

There seems to be a certain amount of confusion surrounding Domain and Forest functional levels.  Microsoft’s own documentation doesn’t always incorporate the latest version information.  KB322692 is a case in point.  I also often hear the words “Native” and “Mixed” in relation to functional levels involving Windows Server 2003 or 2008, when in fact these terms are only relevant in the context of Windows 2000 Server.  Anyway, in an effort to clarify the situation and to avoid confusion I put together the tables below. 

  

Domain Functional Level

Numeric

DCs Supported
Windows 2000 Mixed

0

Windows NT 4.0
Windows 2000 Server
Windows Server 2003
Windows 2000 Native

0

Windows Server 2000
Windows Server 2003
Windows Server 2008
Windows Server 2008 R2
Windows Server 2003 Interim

1

Windows NT 4.0
Windows Server 2003
Windows Server 2003

2

Windows Server 2003
Windows Server 2008
Windows Server 2008 R2
Windows Server 2008

3

Windows Server 2008
Windows Server 2008 R2
Windows Server 2008 R2

4

Windows Server 2008 R2

 

Forest Functional Level

Numeric

DCs Supported
Windows 2000

0

Windows NT 4.0
Windows Server 2000
Windows Server 2003
Windows Server 2008
Windows Server 2008 R2
Windows Server 2003 Interim

1

Windows NT 4.0
Windows Server 2003
Windows Server 2003

2

Windows Server 2003
Windows Server 2008
Windows Server 2008 R2
Windows Server 2008

3

Windows Server 2008
Windows Server 2008 R2
Windows Server 2008 R2

4

Windows Server 2008 R2

The goodness that is Powershell ISE

Earlier this week Nathan Mercer (Technology Advisor for Microsoft) gave me a brief look at the new Powershell Integrated Scripting Environment available with Windows Server 2008 R2.  ISE allows you to run commands and write, test, and debug scripts in a Windows GUI.  It also has multi-line editing, tab completion, syntax coloring, selective execution and context-sensitive help.  The best thing about it from my perspective is that it is great for demos of R2 Active Directory Powershell cmdlets.  Basically, it provides a much nicer view than the powershell command line and the selective execution allows you to pre-load all the commands you need (and save them in a Powershell data file), so that you don’t have to type them in front of a whole bunch of people! 

Powershell ISE

ISE is installed as a feature.  You can use server manager to install ISE, but if you want to go the Powershell route, try the following commands:

> Import-module ServerManager

> Add-WindowsFeature Powershell-ise

To use the feature and launch the GUI simply type the following at the Powershell command prompt:

> ise

Enjoy!

A couple of speaking engagements

Wow, looking at the site I see that I haven’t been blogging for the past two months.  I have a good excuse - I was away for 5 weeks on holiday in Europe.  And since then I’ve been catching up with various things and trying to get prepared for a couple of speaking gigs.

First up is a session on What’s new in Windows Server 2008 R2 Active Directory for the Wellington Infrastructure User Group.  It starts at 5pm on Tuesday 8th September 2009.  Microsoft are hosting and they usually provide beer and pizza, so it’s worth coming along for that, if nothing else.

The second is a break-out session at Microsof Teched in Auckland on Wednesday 16th:

SVR203 Best practices for upgrading Active Directory to Windows Server 2008 R2

Presenter: Tony Murray

 

Wed 9/16 | 12:10-13:25 | New Zealand Room 1

If you’re going to Teched come along and say hello, even if you can’t make the session (it is, after all, the day after TechFest).

Next Page »