Ad account operation C # sample code (1) -- import user information

Source: Internet
Author: User
Tags samaccountname
 

Recently I wrote a small tool for AD account import (why do I write "accounts ?), I would like to share with you the relevant code. Thank you for your advice!

First, I have prepared an Excel file as an import template and added some test data.

Then, I open Visual Studio 2012 and create a new windows form application. On the main form interface, I put some labels, Textbox, button controls, and a progressbar.

 

Start writing code. First, write the method for reading data from Excel.

        private static async Task<DataTable> GetTableFromExcelAsync(string fileName)        {            return await Task.Factory.StartNew<DataTable>(() => GetTableFromExcel(fileName));        }        private static DataTable GetTableFromExcel(string fileName)        {            DataTable dataTable = new DataTable();            string connectionString = string.Format("Provider = Microsoft.ACE.OLEDB.12.0;Data Source ={0};Extended Properties=‘Excel 12.0 Xml;HDR=YES‘", fileName);            using (OleDbConnection oleDbConnection = new OleDbConnection(connectionString))            {                oleDbConnection.Open();                DataTable schemaTable = oleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new Object[] { null, null, null, "TABLE" });                string sheetName = schemaTable.Rows[0].Field<string>("TABLE_NAME");                string commandText = string.Format("select * from [{0}]", sheetName);                using (OleDbDataAdapter adapter = new OleDbDataAdapter(commandText, oleDbConnection))                {                    adapter.Fill(dataTable);                }            }            return dataTable;        }


In this way, the result is saved in a able:

       private async void btnImport_Click(object sender, EventArgs e)       {            DataTable dataTable = await GetTableFromExcelAsync(txtUserListPath.Text);       }


An exception occurred while running: "Microsoft. Ace. oledb.12.0 provider is not registered on the Local Computer ".

 

My system is Windows 8 on x64. After accessdatabaseengine.exe is downloaded, data is read successfully.

Yes: http://download.microsoft.com/download/7/0/3/703ffbcb-dc0c-4e19-b0da-1463960fdcdb/AccessDatabaseEngine.exe

If an exception occurs during release, try setting properties and change the target platform (g) to x86 or select "preferred 32-bit (p )".

To access the AD service in. net, you can use the directoryentry class (reference assembly: system. directoryservices (in system. directoryservices. dll) and namespace: system. directoryservices ).

To create a directoryentry object, you must provide the LDAP address as the root ou of the user we created. Of course, you must have the permission to create the ou and account under this ou.

                string ldapPath = txtLdapPath.Text;                string userName = txtUserName.Text;                string password = txtPassword.Text; 

DirectoryEntry rootDirectoryEntry; if (userName != string.Empty) { rootDirectoryEntry = new DirectoryEntry(ldapPath, userName, password); } else { rootDirectoryEntry = new DirectoryEntry(ldapPath); }

Directoryentry class usage reference: http://msdn.microsoft.com/zh-cn/library/z9cddzaa (V = vs.110). aspx

Before creating user accounts, you must first create the parent ou on which they depend. The code for creating an ou is as follows:

DirectoryEntry currentOuDirectoryEntry = currentOuDirectoryEntry.Children.Add("OU=" + currentValue, "organizationalUnit");currentOuDirectoryEntry.Properties["name"].Add(currentValue);currentOuDirectoryEntry.CommitChanges();

The code for creating a user is as follows:

DirectoryEntry currentUserDirectoryEntry = currentOuDirectoryEntry.Children.Add("CN=" + displayName, "user");currentUserDirectoryEntry.Properties["sAMAccountName"].Value = sAMAccountName;currentUserDirectoryEntry.Properties["userPrincipalName"].Value = string.Format(@"{0}@{1}", sAMAccountName, domainName);currentUserDirectoryEntry.Properties["displayName"].Value = displayName;currentUserDirectoryEntry.CommitChanges();

The properties attribute of the directoryentry class is a set. In addition to some string-type attributes, there are also a few operations that I think are troublesome.

For example, "useraccountcontrol" seems to be an integer field, but in fact it contains many status information. Each status corresponds to an attribute sign (for example, the password never expires at 65536 ). Therefore, the sub-bitwise operation is required to read or write data from this useraccountcontrol field.

        private void SetPropertyInUserAccountControl(DirectoryEntry directoryEntry, bool newValue, int propertyflag)        {            int userAccountControl = (int)directoryEntry.Properties["userAccountControl"].Value;            bool oldValue = GetPropertyFromUserAccountControl(directoryEntry, propertyflag);            if (oldValue != newValue)            {                if (newValue)                {                    directoryEntry.Properties["userAccountControl"].Value = userAccountControl | propertyflag;                }                else                {                    directoryEntry.Properties["userAccountControl"].Value = userAccountControl & ~propertyflag;                }            }        }        private bool GetPropertyFromUserAccountControl(DirectoryEntry directoryEntry, int propertyflag)        {            return Convert.ToBoolean((int)directoryEntry.Properties["userAccountControl"].Value & propertyflag);        }

For more information about the useraccountcontrol attribute flag (propertyflag parameter), see examples (vs.85). aspx. So what do these logo attributes mean? Why can we get the corresponding status by setting the "useraccountcontrol" value "&" to the attribute flag? I converted these attribute flags into binary, and found that there is only one 1, and the others are 0. The position of that 1 is the flag of the State. If the position of the "useraccountcontrol" field is 1, the corresponding state is "true. And then calculate (&: Reference: http://msdn.microsoft.com/zh-cn/library/sbf85k1c.aspx) operation, because 0 & 0 is equal to 0 0 & 1 or 1 & 0 is also equal to 0, only 1 & 1 can be equal to 1, therefore, the calculation of the propertyflag between "useraccountcontrol" and "only one digit is 1 and all others are 0" can infer whether the flag corresponding to this state is 1.

However, I hate this design that saves the status of multiple dimensions in one field. In my previous project, I also met a person who designs a table field like this in a relational database, however, I personally think this is not in line with the design of the first paradigm (the same column has multiple values and should be divided into multiple isxx1 and isxx2 bit fields). In addition, the status is a commonly used filter condition, can I perform index search on this field? Of course, some people think that this reduces the number of fields (should they be separated when the UI is displayed to the user? Who else can understand it !), In addition, if you want to add more States after designing this state field, you do not need to modify the table structure. But the most important thing is that this design can reflect the high level of designers, this is because junior programmers, programmers with poor mathematics, and programmers with poor memory do not immediately know what an integer value represents-I am such a programmer.

But fortunately, we can directly use a few frequently used accounts. I created a normal account and do not need to disable it. Therefore, useraccountcontrol directly gives 512.

There are also these "system. _ comobject" type attributes, which is too inconvenient to operate. I found some information on the Internet, usually referencing an "InterOP. activeds. dll" file (it is not clear who wrote it ). I only want the new user to change the password the next time they log on:

currentUserDirectoryEntry.Properties["pwdLastSet"].Value = new LargeInteger() { HighPart = 0, LowPart = 0 };

However, I did not use the above Code but wrote it like this, And I succeeded.

currentUserDirectoryEntry.Properties["pwdLastSet"].Value = 0;

There is a reference for ADSI object properties: http://msdn.microsoft.com/zh-cn/library/ms180868 (V = vs.90). aspx.

I will write several common string type attributes in an XML file, and assign values directly when importing data.

<Userproperties> <! -- General --> <property name = "Sn" Title = "surname"/> <property name = "givenname" Title = "name"/>
<Property name = "initials" Title = "abbreviation"/> <property name = "displayname" Title = "display name"/> <property name = "telephonenumber" Title =" phone number "/> <property name =" othertelephone "Title =" other phone number "/> <property name =" mail "Title =" email "/> <property name = "Description" Title = "Description"/> <property name = "physicaldeliveryofficename" Title = "office"/> <property name = "wwwhomepage" Title = "webpage"/> <propert Y name = "url" Title = "other webpages"/> • <! -- Address --> <property name = "CO" Title = "country/region"/> <property name = "St" Title = "province/Autonomous Region"/> <property name = "L" Title = "city/county"/> <property name = "streetaddress" Title = "street"/> <property name = "postofficebox" Title = "Postal mailbox "/> <property name = "postalcode" Title = "zip code"/> • <! -- Telephone --> <property name = "homephone" Title = "home phone"/> <property name = "otherhomephone" Title = "other home phone"/> <property name =" pager "Title =" pager "/> <property name =" otherpager "Title =" Other pagers "/> <property name =" mobile "Title =" Mobile Phone "/> <property name = "othermobile" Title = "other mobile phones"/> <property name = "facsimiletelephonenumber" Title = "fax"/> <property name = "otherfacsimiletelephonenumber" tit Le = "Other fax"/> <property name = "ipphone" Title = "IP Phone"/> <property name = "otheripphone" Title = "other IP Phone"/> <property name = "info" Title = "comment"/> • <! -- Account --> <property name = "userprincipalname" Title = "User Login Name"/> <property name = "samaccountname" Title = "User Login Name (earlier than Windows 2000) "/> • <! -- Organization --> <property name = "company" Title = "company"/> <property name = "department" Title = "department"/> <property name = "title" title = "title"/> <property name = "manager" Title = "manager"/> <property name = "directreports" Title = "direct subordinate"/> </userproperties>

If you submit all these attributes at a time, you may also encounter a very personal exception: "the server is unwilling to process this request ".

To make "she" willing, you can write as follows:

using (DirectoryEntry currentUserDirectoryEntry = currentOuDirectoryEntry.Children.Add("CN=" + displayName, "user"))                            {                                currentUserDirectoryEntry.Properties["sAMAccountName"].Value = sAMAccountName;                                currentUserDirectoryEntry.Properties["userPrincipalName"].Value = string.Format(@"{0}@{1}", sAMAccountName, domainName);                                currentUserDirectoryEntry.Properties["displayName"].Value = displayName;                                currentUserDirectoryEntry.CommitChanges();                                currentUserDirectoryEntry.Properties["userAccountControl"].Value = userAccountControl;                                currentUserDirectoryEntry.Properties["pwdLastSet"].Value = 0;                                currentUserDirectoryEntry.Invoke("SetPassword", new object[] { newUserDefaultPassword });                                currentUserDirectoryEntry.CommitChanges();                            }

Because I want to give the newly imported user an initial password, you can modify the password and write it like this:

currentUserDirectoryEntry.Invoke("SetPassword", new object[] { newUserDefaultPassword });

When a user is the administrator of an ou, he or she needs to be granted permissions. Activedirectoryrights in the Code is an enumeration type. Of course, you may also use other options.

If (string. equals (currentdatarow [_ isadmincolumnname] as string, @ "yes") {identityreference newowner = new ntaccount (domainname, samaccountname ). translate (typeof (securityidentifier); activedirectoryaccessrule newrule = new activedirectoryaccessrule (newowner, activedirectoryrights. genericall, accesscontroltype. allow); currentoudirectoryentry. objectsecurity. setaccessrule (newrule); currentoudirectoryentry. commitchanges ();}

If the user to be imported already exists, an exception occurs. How can we determine whether a user already exists? In this case, we need to use the. NET directorysearcher type. A constructor of this type needs to provide a search root path, search filter, search attribute, and search range.

 DirectorySearcher userDirectorySearcher = new DirectorySearcher(currentOuDirectoryEntry, string.Format(@"(&(cn={0})(objectCategory=person)(objectClass=user))", displayName), new[] { "adspath" }, SearchScope.OneLevel);
 SearchResult searchResult = userDirectorySearcher.FindOne();
 if (searchResult != null)
 {
//TODO:......
}

Directorysearcher class usage reference: http://msdn.microsoft.com/zh-cn/library/System.DirectoryServices.DirectorySearcher (V = vs.90). aspx

Finally, combine the scattered code, which is the tool I want to do!

Check the import result. The import is successful.

Of course, this is just a simple small example. We will continue to improve it in the future. If you can see that I am not doing well, please correct me and give me more suggestions. Thank you very much!

Other references:

The http://msdn.microsoft.com/en-us/library/aa367008 (vs.85). aspx

Http://msdn.microsoft.com/en-us/library/windows/desktop/ms675085 (V = vs.85). aspx

Ad user import tool download:

Http://files.cnblogs.com/CSharpDevelopers/ADUserImportTool.zip

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.