The first step to creating a data access layer involves defining how to connect to the data store. Today’s post will begin with a review of the various options available when connecting to a domain controller. We will end by implementing the DbConnectionStringBuilder class which is responsible for translating a connection string into properties and property values into a connection string.
Active Directory Data Access Layer Series
This is the second post of an eight part series about the Active Directory Data Access Layer. As each post builds on the previous it may be helpful to review older posts prior to reading this one. If you would like to download a working copy of the AD DAL please refer to the download on the Code Share page.
- First Post: Active Directory Data Access Layer
- Second Post: Active Directory Connection Strings
- Third Post: AdConnection: Enforcing Active Directory Communication Best Practices
- Fourth Post: AdCommandTextParser: Parsing SQL Statements
- Fifth Post: AdCommand: Running Active Directory Queries
- Sixth Post: AdDataReader: Providing Controlled Access to AD Values
- Seventh Post: AdDataAdapter: Managing Active Directory Data
- Eighth Post: AD Query: Putting it All Together
Establishing a Connection with the DirectoryEntry Object
The DirectoryEntry object accepts four parameters that define how to connect to Active Directory:
- The path,
- A user name,
- A password, and
- A bitwise operator value that specifies the connection options.
Imports System.DirectoryServices Dim dirEntry As New DirectoryEntry("LDAP://DC=LarrySteinle,DC=local", "LSteinle", "********", AuthenticationTypes.Secure OR AuthenticationTypes.Delegate)
In the example the code specifies to connect to the root ou in the LarrySteinle.local domain with the user id, “LSteinle”, using the specified password. The connection is to be a secure connection with delegation.
To better understand all the options that the connection string should support let’s review each argument for the DirectoryEntry instantiation method.
AD Path Syntax
In the post, AD Path Helper, we learned that an Active Directory path consists of a provider, host name, port number and distinguished name. The distinguished name must have at a minimum the domain component (DC=LarrySteinle,DC=local).
provider://hostName:portNumber/distinguishedName
The provider may be one of but not limited to the following values:
- LDAP to connect to Microsoft Active Directory,
- GC to connect to the Active Directory Global Catalog,
- WinNT to connect to the Windows 4.0 legacy provider,
- NDS to connect to a Novell provider,
- NWCOMPAT to connect to legacy Novell providers,
- IIS to connect to Internet Information Services provider and finally
- Ads which can be used to identify the available providers.
The host name can be an IP Address (not recommended), a domain name (recommended) or a domain controller name. When providing a domain name or domain controller name use a fully qualified name. The host name is optional as the system can extrapolate the domain name from the distinguished name. However, I would recommend the use of a host name in the path for the connection anyway.
The port number varies depending on the provider and selected authentication type:
Provider | Authentication Type | Default Port Number |
---|---|---|
LDAP | Standard | 389 |
Secure | 636 | |
GC | Standard | 3268 |
Secure | 3269 |
Check with your administrator to verify if the default port numbers were used or if they were modified. If the default port numbers are used then there is no need to provide the value. But if the port number was changed from the default it must be provided as part of the ldap path in order to establish a connection with the Active Directory domain.
The distinguished name is a collection of relative distinguished names (rdn’s) separated by a comma.
Our connection string class will need to support a provider, user id, password, port number and host name so that a connection can be established.
The connection string will also need to support all the authentication type options to ensure that the correct type of connection is established with the Active Directory domain.
Credentials
The user id and password are both optional arguments. When missing the security context of the current authenticated user is assumed.
Authentication Types
There are numerous connection type options available. Some connection type options require another option to be enabled while others are independent settings.
Authentication Type | Description | Special Instructions |
---|---|---|
Anonymous | Indicates that no security binding is required. | This should never be used. Security binding is required with LDAP and GC providers. |
ReadOnlyServer | Establishes a read-only connection. | This attribute is ignored when connecting to Active Directory. |
Secure | Specifies that the system must bind with Windows Security Support Provider Interface (SSPI) ensuring that the user id and password are stored in an encrypted format. | May be used with the Sealing, Signing, Delegation, FastBind and ServerBind flags. |
Sealing | Ensures that all traffic is encrypted. | Must be used with the Secure option. Not supported with all protocols. The Sealing flag is supported with Kerberos and NTLM on Windows Server 2005 and greater. |
Signing | Ensures that the traffic is not tampered or altered. | Must be used with the Secure option. |
Delegation | Identifies that the security context from another network may be used. | Must be used with the Secure option. |
SecureSocketsLayer | Specifies that SSL/TLS protocol will be used to bind to the domain. | May be used with Secure, Delegation, FastBind and ServerBind flags. May not be used with the Sealing and Signing flags. |
ServerBind | Specifies that the domain controller will be supplied. | |
FastBind | Disables querying the system for the object class. | Increases efficiency as only one read is required per object but does limit the capabilities of the system. Use with caution. |
Now that we have identified all the options and flags required to connect to Active Directory lets discuss the Connection String itself.
The Active Directory Connection String
The complete connection string will use the following syntax:
Provider=value;Data Source=value;Initial Catalog=value;Port=value;User Id=value;Password=value;Integrated Security=value;Sealing=boolean;Signing=boolean;Delegate=boolean;SecureSocketsLayer=boolean;Fast Bind=boolean;ReadOnlyServer=boolean;ServerBind=boolean;
To make it easier to construct the connection string our system will support the following key name aliases:
Key Name | Alias |
---|---|
Data Source | Domain Name, Server, Address, Addr, or Network Address |
Initial Catalog | Database or Domain Controller |
Port | Port Number |
User Id | UID |
Password | PWD |
Integrated Security | Secure, Trusted_Connection |
SecureSocketsLayer | TrustServerCertificate |
AdConnectionStringBuilder
The AdConnectionStringBuilder was introduced with VS.Net 2008 (CLR 2.0). This base class simplifies managing connection strings. An example of the AdConnectionStringBuilder in use can be found with the SqlConnectionStringBuilder in the System.Data.SQLClients namespace.
The AdConnectionStringBuilder class is extremely simple to implement. Simply assign the value to the base ConnectionString property. The connection string is parsed into a dictionary where each key and value can be accessed using the Item, Keys and Values properties. The connection string can easily be modified using the Add and Remove methods.
Now that we have established what information needs to be tracked in the connection string we can create our AdConnectionStringBuilder class.
Namespace Data.ActiveDirectory ''' <summary> ''' Provides a simple way to create and manage the contents of ''' connection strings used by the AdConnectionString class. ''' </summary> ''' <remarks></remarks> Public Class AdConnectionStringBuilder Inherits System.Data.Common.DbConnectionStringBuilder #Region "Constructor Section" ''' <summary> ''' Instantiates a new instance of the AdConnectionStringBuilder class. ''' </summary> ''' <remarks></remarks> Public Sub New() DomainName = DefaultDomainName DomainController = GetDefaultDomainController(DomainName) Secure = True ServerBind = True End Sub ''' <summary> ''' Instantiates a new instance of the AdConnectionStringBuilder class. ''' </summary> ''' <param name="connectionString"> ''' The connection string. ''' </param> ''' <remarks></remarks> Public Sub New(ByVal connectionString As String) Me.ConnectionString = connectionString End Sub #End Region #Region "Provider Information Section" ''' <summary> ''' Gets a value for the .Net Data Provider. ''' </summary> ''' <remarks></remarks> Public ReadOnly Property ApplicationName As String Get 'Should return LarrySteinle.Data.ActiveDirectory Return Me.GetType.FullName.Replace("AdConnectionStringBuilder", String.Empty) End Get End Property ''' <summary> ''' Gets the name of the provider for the AdConnectionStringBuilder. ''' </summary> ''' <remarks> ''' May be one of but not excluded to the following values: ''' <list> ''' <item>LDAP: Active Directory (default).</item> ''' <item>GC: Active Directory Global Catalog.</item> ''' WinNT: Windows NT 4 Domain Controllers and local SAM databases. ''' <item>NDS: Novell Directory Services.</item> ''' NWCOMPAT: Older Novell Services. ''' <item>IIS: Internet Information Services metabase.</item> ''' <item>Ads: Enumerate installed directory providers.</item> ''' </list> ''' </remarks> Public Property Provider As String Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Provider", holdValue) Then Return holdValue.ToString.Trim.ToUpper Else Return "LDAP" End If End Get Set(ByVal value As String) If String.IsNullOrWhiteSpace(value) Then MyBase.Remove("Provider") Else MyBase.Add("Provider", value) End If End Set End Property #End Region #Region "Connection Section" ''' <summary> ''' Get an instance of the root path. ''' </summary> ''' <remarks></remarks> Public ReadOnly Property RootPath As String Get Return String.Format("{0}://{1}/{2}", Provider, IIf(ServerBind, DomainController, DomainName), RootDN) Return String.Format("{0}://{1}:{2}/{3}", Provider, IIf(ServerBind, DomainController, DomainName), PortNumber, RootDN) End Get End Property ''' <summary> ''' Get an instance of the DomainContext domain components (DC). ''' </summary> ''' <remarks></remarks> Public ReadOnly Property RootDN As String Get Dim dc As String = String.Empty For Each domainNamePart As String In DomainName.Split("."c) If Not String.IsNullOrWhiteSpace(domainNamePart) Then dc &= ",DC=" & domainNamePart.Trim End If Next If dc.StartsWith(",") Then dc = dc.Substring(1) Return dc End Get End Property ''' <summary> ''' Gets the Default Domain Controller (Primary Domain Controller) for the ''' specified domain name. ''' </summary> ''' <param name="<span class=" />domainName"> ''' The name of the domain to query for the domain controller. ''' </param> ''' <returns> ''' A fully-qualified domain controller name. ''' </returns> ''' <remarks></remarks> Public Function GetDefaultDomainController(ByVal domainName As String) As String If String.IsNullOrWhiteSpace(domainName) Then Return System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain.FindDomainController.Name Else Dim dirContext As New System.DirectoryServices.ActiveDirectory.DirectoryContext(DirectoryServices.ActiveDirectory.DirectoryContextType.Domain, domainName) Return DirectoryServices.ActiveDirectory.Domain.GetDomain(dirContext).FindDomainController.Name End If End Function ''' <summary> ''' Gets the domain name for the current context that the computer is running. ''' </summary> ''' <remarks></remarks> Public ReadOnly Property DefaultDomainName As String Get Return System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain.Name End Get End Property ''' <summary> ''' Gets or sets the name of the domain to connect. ''' </summary> ''' <returns> ''' Returns the domain name if available otherwise returns the default domain name. ''' </returns> ''' <remarks></remarks> Public Property DomainName As String Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Domain Name", holdValue) _ OrElse MyBase.TryGetValue("Data Source", holdValue) _ OrElse MyBase.TryGetValue("Server", holdValue) _ OrElse MyBase.TryGetValue("Address", holdValue) _ OrElse MyBase.TryGetValue("Addr", holdValue) _ OrElse MyBase.TryGetValue("Network Address", holdValue) Then If String.Compare(holdValue.ToString.Trim, "localhost") <> 0 Then Return holdValue.ToString End If End If Return DefaultDomainName End Get Set(ByVal value As String) MyBase.Remove("Domain Name") MyBase.Remove("Domain Source") MyBase.Remove("Server") MyBase.Remove("Address") MyBase.Remove("Addr") MyBase.Remove("Network Address") If Not String.IsNullOrWhiteSpace(value) Then MyBase.Add("Domain Name", value) End Set End Property ''' <summary> ''' Gets or sets the name of the specific domain controller to connect. ''' </summary> ''' <returns> ''' Returns the domain controller if available otherwise returns the default domain ''' controller for the specified domain name. ''' </returns> ''' <remarks> ''' If ServerBind is specified and a server name is not supplied for the ''' Domain Controller then a default domain controller will automatically ''' be found. Because the system detects errors in domain controllers and ''' reconnects as needed it is recommended to use ServerBind without ''' specifying a domain controller. ''' </remarks> Public Property DomainController As String Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Domain Controller", holdValue) _ OrElse MyBase.TryGetValue("Initial Catalog", holdValue) _ OrElse MyBase.TryGetValue("Database", holdValue) Then If String.Compare(holdValue.ToString.Trim, "localhost") = 0 Then Return GetDefaultDomainController(DomainName) Else Return holdValue.ToString End If ElseIf ServerBind Then 'If a server is not provided and ServerBind is specified 'lookup the default domain controller and pass it in. Return GetDefaultDomainController(DomainName) Else 'Indicates that the service should find the domain to connect Return String.Empty End If End Get Set(ByVal value As String) MyBase.Remove("Database") MyBase.Remove("Initial Catalog") MyBase.Remove("Domain Controller") If Not String.IsNullOrWhiteSpace(value) Then MyBase.Add("Domain Controller", value) End Set End Property ''' <summary> ''' Gets or sets the port number to connect. ''' </summary> ''' <returns> ''' Returns the default port number for LDAP and GC providers. ''' If the provider is not recognized and a port number has ''' not been provided a negative value is returned. ''' </returns> ''' <remarks></remarks> Public Property PortNumber As Integer Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Port", holdValue) _ OrElse MyBase.TryGetValue("Port Number", holdValue) Then If Not IsNumeric(holdValue) Then Return Convert.ToInt32(holdValue) End If End If If Provider = "LDAP" Then Return DirectCast(IIf(SecureSocketsLayer, 636, 389), Integer) ElseIf Provider = "GC" Then Return DirectCast(IIf(SecureSocketsLayer, 3269, 3268), Integer) Else Return -1 End If End Get Set(ByVal value As Integer) MyBase.Remove("Port") MyBase.Remove("Port Number") If value > 0 Then MyBase.Add("Port", value) End Set End Property #End Region #Region "Credential Management Section" ''' <summary> ''' Gets or sets the name of the user account to use for security context. ''' </summary> ''' <remarks></remarks> Public Property UserId As String Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("User Id", holdValue) _ OrElse MyBase.TryGetValue("UID", holdValue) Then Return holdValue.ToString Else Return Nothing End If End Get Set(ByVal value As String) MyBase.Remove("UID") MyBase.Remove("User Id") If Not String.IsNullOrWhiteSpace(value) Then MyBase.Add("User Id", value) End Set End Property ''' <summary> ''' Gets or sets the password for the user account to use for security context. ''' </summary> ''' <remarks></remarks> Public Property Password As String Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Password", holdValue) _ OrElse MyBase.TryGetValue("Pwd", holdValue) Then Return holdValue.ToString Else Return Nothing End If End Get Set(ByVal value As String) MyBase.Remove("Pwd") MyBase.Remove("Password") If Not String.IsNullOrWhiteSpace(value) Then MyBase.Add("Password", value) End Set End Property #End Region #Region "Manage Authentication Types" ''' <summary> ''' Gets or sets the connection string authentication type options. ''' </summary> ''' <value></value> ''' <returns></returns> ''' <remarks></remarks> Public Property AuthenticationType As DirectoryServices.AuthenticationTypes Get Dim authTypes As DirectoryServices.AuthenticationTypes = DirectoryServices.AuthenticationTypes.None Dim delegationState As Boolean = Delegation Dim sealingState As Boolean = Sealing Dim signingState As Boolean = Signing Dim securityState As Boolean = Secure If SecureSocketsLayer Then delegationState = False sealingState = False signingState = False End If If delegationState OrElse sealingState OrElse signingState Then securityState = True End If If SecureSocketsLayer Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.SecureSocketsLayer If securityState Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.Secure If delegationState Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.Delegation If sealingState Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.Sealing If signingState Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.Signing If ServerBind Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.ServerBind If FastBind Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.FastBind If ReadOnlyServer Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.ReadonlyServer If Anonymous Then authTypes = authTypes Or DirectoryServices.AuthenticationTypes.Anonymous Return authTypes End Get Set(ByVal value As DirectoryServices.AuthenticationTypes) Anonymous = ((value And DirectoryServices.AuthenticationTypes.Anonymous) = DirectoryServices.AuthenticationTypes.Anonymous) Delegation = ((value And DirectoryServices.AuthenticationTypes.Delegation) = DirectoryServices.AuthenticationTypes.Delegation) Encryption = ((value And DirectoryServices.AuthenticationTypes.Encryption) = DirectoryServices.AuthenticationTypes.Encryption) FastBind = ((value And DirectoryServices.AuthenticationTypes.FastBind) = DirectoryServices.AuthenticationTypes.FastBind) ReadOnlyServer = ((value And DirectoryServices.AuthenticationTypes.ReadonlyServer) = DirectoryServices.AuthenticationTypes.ReadonlyServer) Sealing = ((value And DirectoryServices.AuthenticationTypes.Sealing) = DirectoryServices.AuthenticationTypes.Sealing) Secure = ((value And DirectoryServices.AuthenticationTypes.Secure) = DirectoryServices.AuthenticationTypes.Secure) SecureSocketsLayer = ((value And DirectoryServices.AuthenticationTypes.SecureSocketsLayer) = DirectoryServices.AuthenticationTypes.SecureSocketsLayer) ServerBind = ((value And DirectoryServices.AuthenticationTypes.ServerBind) = DirectoryServices.AuthenticationTypes.ServerBind) Signing = ((value And DirectoryServices.AuthenticationTypes.Signing) = DirectoryServices.AuthenticationTypes.Signing) End Set End Property ''' <summary> ''' Gets or sets a value that indicates that no security binding is required. ''' </summary> ''' <remarks> ''' Avoid using as Active Directory permissions make the option pretty useless. ''' </remarks> Public Property Anonymous As Boolean Get If Not Secure AndAlso Not SecureSocketsLayer AndAlso Not Delegation AndAlso Not Sealing _ AndAlso String.IsNullOrWhiteSpace(UserId) _ AndAlso String.IsNullOrWhiteSpace(Password) Then Return True Else Return False End If End Get Set(ByVal value As Boolean) If value Then Secure = False SecureSocketsLayer = False Delegation = False Sealing = False UserId = Nothing Password = Nothing End If End Set End Property ''' <summary> ''' Gets or sets a value indicating that the security context ''' from another network may be used. ''' </summary> ''' <remarks> ''' Must be used with the Secure flag. ''' </remarks> Public Property Delegation As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Delegate", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) If value Then MyBase.Add("Delegate", value) Else MyBase.Remove("Delegate") End If End Set End Property ''' <summary> ''' Get a value indicating that a secure socket is required. ''' </summary> ''' <remarks></remarks> Public Property Encryption As Boolean Get Return SecureSocketsLayer End Get Set(ByVal value As Boolean) Throw New NotSupportedException("Deprecated connection type. Use SecureSocketsLayer instead.") End Set End Property ''' <summary> ''' Gets or sets a value that disables querying for the object class. ''' </summary> ''' <remarks> ''' Increases performance by restricting queries to one operation instead of two. ''' Use with caution as Fast Binding limits the capabilities of the system. ''' </remarks> Public Property FastBind As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Fast Bind", holdValue) _ OrElse MyBase.TryGetValue("FastBind", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) MyBase.Remove("Fast Bind") MyBase.Remove("FastBind") If value Then MyBase.Add("Fast Bind", value) End Set End Property ''' <summary> ''' Gets or sets a value that establishes a read-only connection. ''' </summary> ''' <remarks> ''' Currently has no impact on Microsoft AD. ''' </remarks> Public Property ReadOnlyServer As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("ReadOnlyServer", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) If value Then MyBase.Add("ReadOnlyServer", value) Else MyBase.Remove("ReadOnlyServer") End If End Set End Property ''' <summary> ''' Gets or sets a value encrypting secure trafic. ''' </summary> ''' <remarks> ''' Not supported with all authentication protocols. Is supported ''' with Kerberos and NTLM on Windows Server 2005 and greater. ''' </remarks> Public Property Sealing As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Sealing", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) If value Then MyBase.Add("Sealing", value) Else MyBase.Remove("Sealing") End If End Set End Property ''' <summary> ''' Gets or sets a value indicating to connect with Windows Security Support ''' Provider Interface (SSPI). ''' </summary> ''' <remarks> ''' Ensures that credentials are stored in encrypted text. Supports connections ''' with both explicit credentials and the current windows security context. ''' To encrypt traffic use with Sealing and to ensure traffic is not tampered ''' with use with Signing. ''' </remarks> Public Property Secure As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Secure", holdValue) _ OrElse MyBase.TryGetValue("Integrated Security", holdValue) _ OrElse MyBase.TryGetValue("Trusted_Connection", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON", "SSPI" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) MyBase.Remove("Integrated Security") MyBase.Remove("Trusted_Connection") MyBase.Remove("Secure") If value Then MyBase.Add("Secure", value) End Set End Property ''' <summary> ''' Gets or sets a value that specifies that SSL / TLS Protocol will be used to ''' encrypt the traffic. ''' </summary> ''' <remarks> ''' May be used with Secure flag but not Sealing or Signing flags. ''' </remarks> Public Property SecureSocketsLayer As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("SecureSocketsLayer", holdValue) _ OrElse MyBase.TryGetValue("TrustServerCertificate", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON", "SSPI" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) MyBase.Remove("SecureSocketsLayer") MyBase.Remove("TrustServerCertificate") If value Then MyBase.Add("SecureSocketsLayer", value) End Set End Property ''' <summary> ''' Gets or sets a value that indicates that an exact server bind is being provided. ''' </summary> ''' <remarks></remarks> Public Property ServerBind As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("ServerBind", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON", "SSPI" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) If value Then MyBase.Add("ServerBind", value) Else MyBase.Remove("ServerBind") End If End Set End Property ''' <summary> ''' Gets or sets a value indicating to sign secure traffic to determine if the data has been tampered with. ''' </summary> ''' <remarks> ''' Requires a Secure connection. ''' </remarks> Public Property Signing As Boolean Get Dim holdValue As Object = Nothing If MyBase.TryGetValue("Signing", holdValue) Then Select Case holdValue.ToString.Trim.ToUpper Case "YES", "TRUE", "ON" : Return True End Select End If Return False End Get Set(ByVal value As Boolean) If value Then MyBase.Add("Signing", value) Else MyBase.Remove("Signing") End If End Set End Property #End Region End Class End Namespace
Summary
In today’s post we reviewed how to connect to Active Directory using the DirectoryEntry object, what the various requirements are to connect to Active Directory and created our first, custom Connection String Builder helper class.
Hi to every one, the contents existing at this web site
are really awesome for people experience, well, keep up the
nice work fellows.
Comment by simcity 5 crack — June 16, 2013 @ 6:07 pm |
Howdy are using WordPress for your site platform? I’m new to the blog world but I’m trying to get started and
set up my own. Do you require any html coding knowledge
to make your own blog? Any help would be greatly appreciated!
Comment by Pauline — July 6, 2013 @ 5:57 pm |
It is very simple. There is absolutely no need to know HTML. However, it never hurts to know more than necessary!
Comment by Larry Steinle — August 17, 2013 @ 8:07 pm |