Introduction
I recently began a project which involved installing a .msi package on approximately 450 computers. The organization previously implemented projects like this by visiting each desktop, but I suggested AD group policy instead. For a number of reasons, I needed to implement the GPO on the computer node, but there were two main problems: I had a list of user accounts but not computers, and I needed to move the PCs from the Computers OU to the new OU that was linked with my software distribution GPO.
Getting Started
The first step was to start capturing which PCs were tied to my list of known users. I created a *simple* logon script that captured the user name and the computer name, and wrote that to a log file:
@echo off
REM collect user name and computer name
echo %username%,%computername% >> \\servername\sharename\userpclog.txt
After about a month, I had 3500 entries. I imported the file into SQL Server, and then created a View to give me my PC-to-user list. There were 450 computers for 398 users (some users such as interns, temp staff, etc. use any available PC). I did not have info on 28 users but that I could deal with.
Move Active Directory Objects
The basic process for moving the PC from one OU to another is to create a connection to the database, get the list of PC names, then loop through the dataset and create a new DirectoryEntry
instance with the current record as the Common Name (CN) in the LDAP path.
First, create a new console application in Visual Studio 2005. You will need to add references to System.DirectoryServices
and System.Data
. By default, you will have a file called Program.cs. Replace the contents of that file with the code below, and replace the connection to the database, command string, and LDAP paths as needed.
using System;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.DirectoryServices;
class ChangeOU
{
static void Main(string[] args)
{
string m_movetopath = "LDAP://OU=newtestou," +
"DC=northamerica,DC=fabrikam,DC=com";
DirectoryEntry DeMoveTo = new DirectoryEntry(m_movetopath);
string ConnectionString = "server=myservername;
Trusted_Connection=yes; database=Users";
string CommandString = "SELECT computername FROM vwUserpc";
SqlDataAdapter sda = new SqlDataAdapter(CommandString,
ConnectionString);
DataSet ds = new DataSet();
sda.Fill(ds);
DataTable dt = ds.Tables[0];
foreach (DataRow dr in dt.Rows)
{
string pc = (string)dr["computername"].ToString();
pc.Trim();
string MovePath = "LDAP://CN=" + pc +
",OU=testou,DC=northamerica," +
"DC=fabrikam,DC=com";
try
{
DirectoryEntry DeMoveFrom = new DirectoryEntry(MovePath);
DeMoveFrom.MoveTo(DeMoveTo);
}
catch (Exception ex)
{
Console.Write(ex.Message);
Console.Write(pc);
}
}
Console.Write("\r\n");
Console.WriteLine("Completed.");
}
}
One thing that is fairly important to note is that I am a domain admin, so creating the DirectoryEntry
instances works "without" security; otherwise, you will need to add the security parameters.
Conclusions
The code may seem pretty straightforward, yet I could not find any examples like this. So, I am writing it up for future reference. I hope you find it useful.
Note: Part of this project also involved scripting an ODBC connection in the registry. These scripts are included in the source files.