Zoho Banner September 2011

Posts tagged ‘Active Directory’

After an absence of a couple of years, the Wellington Windows Infrastructure User Group has been brought back to life!  If you’re in Wellington on Wednesday 4th July, come along and watch me and Daniel Bowbyes presenting on some Windows Server 2012 goodness.

http://www.mscommunities.co.nz/Events/Wellington-Windows-Infrastructure-User-Group—Win.aspx

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.

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

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!

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

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.

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 other day I was in an environment where I had to find what Certification Authorities (CAs) were in place.  With nobody immediately available to help me out, I stumbled around for bit before I worked out how to find them.

Method 1

Query the membership of the Cert Publishers group.  Cert Publishers is a built-in AD group.  When you create a new CA on a member server or a DC, the computer will be added to the group membership.

cert-publishers.jpg

While it worked for me in terms of identifying the server names the CAs were hosted on, it did not provide me with the CA names themselves.   In any case, I’m not convinced this is wholly reliable method of finding servers that host CAs, because there is always the potential for someone with permissions to manually edit the Cert Publishers group membership.  Also, I’m not sure what happens if someone does an ugly decommissioning of a CA.  Does the membership get cleaned up?  Probably not.

Method 2.

Search Active Directory for objects with an objectClass of certificationAuthority.  These are stored in the Configuration partition under CN=Certification Authorities,CN=Public Key Services,CN=Services,CN=Configuration,<ForestRootDN>.  Here’s an example of how to find them using adfind.exe.

C:\>adfind -b “CN=Certification Authorities,CN=Public Key Services,CN=Services,C
N=Configuration,DC=widget,DC=com” -f (objectclass=certificationAuthority) 1.1

The problem with looking in AD is that it provides you with the name of the CA, but not the server that’s hosting it.  Ok, in my example the server name is part of the CA name, but this may not always be the case.   The server name is probably buried within the cACertificate attribute of the certificationAuthority object, which is unfortunately not human-readable.

Method 3.

Open a command prompt and type certutil – dump.  You will see output similar to that shown below.

 Entry 0: (Local)
  Name:                    `widget-ADLDS1-CA’
  Organizational Unit:     `’
  Organization:            `’
  Locality:                `’
  State:                   `’
  Country/region:          `’
  Config:                  `ADLDS1.widget.com\widget-ADLDS1-CA’
  Exchange Certificate:    `’
  Signature Certificate:   `ADLDS1.widget.com_widget-ADLDS1-CA.crt’
  Description:             `’
  Server:                  `ADLDS1.widget.com’
  Authority:               `widget-ADLDS1-CA’
  Sanitized Name:          `widget-ADLDS1-CA’
  Short Name:              `widget-ADLDS1-CA’
  Sanitized Short Name:    `widget-ADLDS1-CA’
  Flags:                   `13′

Entry 1:
  Name:                    `widget-RWDC1-CA’
  Organizational Unit:     `’
  Organization:            `’
  Locality:                `’
  State:                   `’
  Country/region:          `’
  Config:                  `RWDC1.widget.com\widget-RWDC1-CA’
  Exchange Certificate:    `’
  Signature Certificate:   `’
  Description:             `’
  Server:                  `RWDC1.widget.com’
  Authority:               `widget-RWDC1-CA’
  Sanitized Name:          `widget-RWDC1-CA’
  Short Name:              `widget-RWDC1-CA’
  Sanitized Short Name:    `widget-RWDC1-CA’
  Flags:                   `1′
CertUtil: -dump command completed successfully.

This shows me that I have two CAs and provides me with information about the CA names and what servers they are hosted on.  But what if I wanted to find out what type of CA they are (i.e. Enterprise or Stand Alone and whether it is a root or subordinate CA)?  The certutil.exe tool can help with that too.  Here’s an example using certutil with the -cainfo parameter.

C:\>certutil -cainfo -config RWDC1.widget.com\widget-RWDC1-CA type

CA type: 0 — Enterprise Root CA
    ENUM_ENTERPRISE_ROOTCA — 0
CertUtil: -CAInfo command completed successfully.

This tells me that my CA running on server RWDC1.widget.com is an Enteprise Root CA.

The syntax of the certutil.exe tool takes a bit of getting used to, but otherwise seems to do the job nicely!

Tony

I have recently spent some time working with Xpath queries as part of Event Log filtering in Windows Server 2008.  It’s a great feature, but one limitation I found was that it doesn’t appear possible to use the starts-with() function when querying Event Logs with either the UI or Wevtutil.exe.  Here’s an example.

Let’s say I enabled LDAP logging on a DC and want to filter the Directory Service event log to find all LDAP queries coming from a particular IP address.  The IP address is buried in one of the Data nodes of the Event XML, as shown in red below.

<Event xmlns=”http://schemas.microsoft.com/win/2004/08/events/event”>

  <System>

    <Provider Name=”Microsoft-Windows-ActiveDirectory_DomainService” Guid=”{0e8478c5-3605-4e8c-8497-1e730c959516}” EventSourceName=”NTDS General” />

    <EventID Qualifiers=”16384″>1644</EventID>

    <Version>0</Version>

    <Level>4</Level>

    <Task>15</Task>

    <Opcode>0</Opcode>

    <Keywords>0×8080000000000000</Keywords>

    <TimeCreated SystemTime=”2009-04-29T20:39:00.886Z” />

    <EventRecordID>339453</EventRecordID>

    <Correlation />

    <Execution ProcessID=”648″ ThreadID=”792″ />

    <Channel>Directory Service</Channel>

    <Computer>DC1.Myco.Com</Computer>

    <Security UserID=”S-1-5-21-854245398-152049171-725345543-4606″ />

  </System>

  <EventData>

    <Data>CN=MyCo Enterprise Issuing CA 1, CN=Public Key Services,CN=Services,CN=Configuration,DC=MyCo,DC=Com</Data>

    <Data> (objectClass=cRLDistributionPoint) </Data>

    <Data>1</Data>

    <Data>1</Data>

    <Data>192.168.40.10:4048</Data>

    <Data>base</Data>

    <Data>deltaRevocationList</Data>

    <Data>

    </Data>

  </EventData>

</Event>

So if I wanted to use Xpath to filter all events in the Directory Service Event Log from that IP address my query would look something like this:

<QueryList>

  <Query Id=”0″ Path=”Directory Service”>

    <Select Path=”Directory Service”>*[System[(Level=4 or Level=0) and (EventID=1644)]] and *[EventData[Data[5]=’192.168.40.10:4048′]]</Select>

  </Query>

</QueryList>

The query works well, but the problem is that the Data node within the XML contains the port number (4048) in addition to the IP address.  I want to find all queries issued from that client, regardless of the port used.  Here’s my attempt to use the starts-with() function to filter the event.

<QueryList>

  <Query Id=”0″ Path=”Directory Service”>

    <Select Path=”Directory Service”>*[System[(Level=4 or Level=0) and (EventID=1644)]] and *[EventData[starts-with(Data[5],’192.168.40.10′)]]</Select>

  </Query>

</QueryList>

This fails with the error “The specified query is invalid“.  Back to the drawing board.  I posted a question to Technet Forums and got some good help from Ivan Ting at Microsoft.  He provided some Javascript that used starts-with() and this worked (after some fun messing around with default namespace issues).  Being something of a Javascript muppet (the antithesis of a Javascript guru), I decided to try my hand at a Powershell version.  Here’s what I came up with.

#####
# Author: Tony Murray
# File name: LDAPEvents.ps1
# Date: 28th April 2009
# Purpose: Extracts LDAP Search information from Directory Service Event
# Log. Requires LDAP logging to be switched on.
#####

# Function to create an object for XML document navigation
# Source: Technet Scriptcenter
function get-xpn ($text)
{
$rdr = [System.IO.StreamReader] $text
$trdr = [system.io.textreader]$rdr
$xpdoc = [System.XML.XPath.XPathDocument] $trdr
$xpdoc.CreateNavigator()
}

# Run Wevtutil.exe to export the Event log to file
# Could use Powershell to do this but it creates odd-looking xml!

$file = “c:\util\dumplog.xml”
& $env:windir\System32\wevtutil qe `”Directory Service` /e:Events | Out-File $file

# Remove the namespace from the xml file. It won’t work if it stays

$findStr = ” xmlns=`’http://schemas.microsoft.com/win/2004/08/events/event`’”
$ReplStr = “”
$newcontent = (Get-Content $file) -replace ($findStr,$ReplStr)
Set-Content $file $newcontent

# Invoke the navigator
$xb = get-xpn $file

# Define the Xpath query we want to use
$query = “//*[ System[(Level=4 or Level=0) and (EventID=1644)] `
and EventData[starts-with(Data[5],’192.168.40.10′)]]”

# Create a CSV file with the output. Each line represents the details
# we want from a single Event.

Write-Output $xb.Select($query) | %{[xml] $_.OuterXml} | Select-Object `
@{name = “Date&Time”;Expression = {$_.Event.System.TimeCreated.SystemTime}}, `
@{name = “SearchBase”;Expression = {$_.Event.EventData.Data[0]}}, `
@{name = “Filter”;Expression = {$_.Event.EventData.Data[1]}}, `
@{name = “Visited”;Expression = {$_.Event.EventData.Data[2]}}, `
@{name = “Returned”;Expression = {$_.Event.EventData.Data[3]}}, `
#@{name = “SourceIP”;Expression = {$_.Event.EventData.Data[4]}}, `
@{name = “SearchScope”;Expression = {$_.Event.EventData.Data[5]}} `
| export-csv ds.csv -notype
# Replace the previous line with the following line to change the output format
#ConvertTo-HTML | Out-File “LDAPEvent.html”

Having to write a script is more effort than simply issuing the query from within Eventvwr, but it does have the advantage of allowing you to return only the information you are interested in – and in the format that you want.  Hopefully, my experience will save you a bit of time and effort if you are trying to achieve something similar.