Directory Services

Example Code for Creating a Group

The following code example contains a function that creates a group with only the essential properties explicitly set (cn, sAMAccountType, groupType) and containing no members:

////////////////////////////////////////////////////////////////////////////////////////////////////
/*  CreateSimpleGroup()   - Function for creating a basic group

	Parameters
 
		IDirectoryObject *pDirObject	-   Parent Directory Object for the new group
		LPWSTR pwCommonName			 -   Common Name for the new group
		IADs ** ppObjRet																														-   Pointer to the Pointer which will receive the new Group
		int iGroupType				-   Bitflags for new group:
																	ADS_GROUP_TYPE_GLOBAL_GROUP, 
																	ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP, 
																	ADS_GROUP_TYPE_UNIVERSAL_GROUP, 
																	ADS_GROUP_TYPE_SECURITY_ENABLED 
*/
HRESULT CreateSimpleGroup(IDirectoryObject *pDirObject, LPWSTR pwCommonName,LPWSTR pwSamAcctName,IADs *ppObjRet,int iGroupType)
{
	if(!pDirObject)
		return E_INVALIDARG;
 
// Verify that the group type is Universal Security
// if true, ensure that domain is in native mode.
	if(((iGroupType & ADS_GROUP_TYPE_UNIVERSAL_GROUP)==ADS_GROUP_TYPE_UNIVERSAL_GROUP)&&((iGroupType & ADS_GROUP_TYPE_SECURITY_ENABLED)==ADS_GROUP_TYPE_SECURITY_ENABLED))
	{
		// Verify that the domain that contains the container is in mixed mode.
		hr = CheckDomainModeOfObject(pDirObject, &bIsMixed);
 
		if (SUCCEEDED(hr))
		{
			if (bIsMixed)
				return E_INVALIDARG;
	}
		else
		{
			return hr;
	}
}
 
	// SamAccountName cannot be larger than 20 characters.
	if (wcslen(pwSamAcctName) >20)
	{
		return E_FAIL;
}
 
	HRESULT	hr;

	/* 
		ADSVALUE is used to specify attribute data for calling IDirectoryObject::CreateDSObject()
		When Creating a new group, the required attributes are : objectClass,sAMAccountName and
		groupType.
 
		In this function the "objectClass" is set to "group". 
		The  "sAMAccountName" is the Windows NT 4.0 name.
		In Windows 2000/Windows NT 4 mixed-mode environment, this attribute
		is exposed to the Windows NT 4 computers. Therefore, this name must be 
		globally unique throughout the network and cannot exceed 20 characters in length.
	*/
	ADSVALUE   sAMValue;
	ADSVALUE   classValue;
	ADSVALUE   groupType;
 
	LPDISPATCH pDisp;
	WCHAR	 *pwCommonNameFull;

	// Build an Array of ADS_ATTR_INFO structures
	// Be aware that the sAMAccountName and groupType entries contain 
	// a pointer to the respective ADSVALUE structures defined previously.
	ADS_ATTR_INFO  attrInfo[] = 
	{  
	 { L"objectClass", ADS_ATTR_UPDATE, 
						 ADSTYPE_CASE_IGNORE_STRING, &classValue, 1 },
	 {L"sAMAccountName", ADS_ATTR_UPDATE, 
						 ADSTYPE_CASE_IGNORE_STRING, &sAMValue, 1},
	 {L"groupType", ADS_ATTR_UPDATE, 
						 ADSTYPE_CASE_IGNORE_STRING, &groupType, 1}
};
 
	// Get the size of the array. 
	DWORD dwAttrs = sizeof(attrInfo)/sizeof(ADS_ATTR_INFO); 
 
	/*
		For ADSVALUES, the dwType member and 
		the data value member (in this case "CaseIgnoreString")
		must be set.
	*/
 
	// To create a group, this parameter must be set to "group".
	classValue.dwType = ADSTYPE_CASE_IGNORE_STRING;
	classValue.CaseIgnoreString = L"group";
 
	// Set sAMAccountName to the name passed to this function
	sAMValue.dwType=ADSTYPE_CASE_IGNORE_STRING;
	sAMValue.CaseIgnoreString = pwSamAcctName;
 
	// Set the groupType to the group type passed to this function
	groupType.dwType=ADSTYPE_INTEGER;
	groupType.Integer =  iGroupType;

	// Allocate a buffer to hold the full common name string
	pwCommonNameFull = new WCHAR[wcslen(pwCommonName)+4];

	// Be aware that CN is limited to 64 characters.
	// Take the passed commonname and prefix a 'CN=' to conform 
	// to the format that  IDirectoryObject::CreateDSObject() requires
	wsprintfW(pwCommonNameFull,L"CN=%s",pwCommonName);
 
	// Create the new group.
	hr = pDirObject->CreateDSObject( pwCommonNameFull,  attrInfo, 
								dwAttrs, &pDisp );
	if (SUCCEEDED(hr))
	{
		// Query the new group for an IADs to be returned from this function.
		hr = pDisp->QueryInterface(IID_IADs,(void**) ppObjRet);
 
		pDisp->Release();
		pDisp = NULL;
}
	return hr;
}
 
 
HRESULT GetDomainMode(IADs *pDomain, BOOL *bIsMixed)
{
HRESULT hr = E_FAIL;
VARIANT var;
if (pDomain)
{
	VariantClear(&var);
	// Get the ntMixedDomain attribute
	hr = pDomain->Get(CComBSTR("ntMixedDomain"), &var);
	if (SUCCEEDED(hr))
	{
		// Type should be VT_I4.
		if (var.vt==VT_I4)
		{
			// Zero (0) indicates native mode.
			if (var.lVal == 0)
				*bIsMixed = FALSE;
			// One (1) indicates mixed mode.
			else if (var.lVal == 1)
				*bIsMixed = TRUE;
			else
				hr=E_FAIL;
	}
}
	VariantClear(&var);
}
return hr;
 
}
 
 
HRESULT CheckDomainModeOfObject(IDirectoryObject *pDirObject, BOOL *bIsMixed)
{
	HRESULT hr = E_FAIL;
	IADs *pDomain = NULL;
	VARIANT VarTest;
	WCHAR *pFound = NULL;
	int iLen;
	WCHAR *pDomainPath = new WCHAR[MAX_PATH*2];
 
	// Verify that the domain that contains the container is in mixed mode
	WCHAR *pVal = NULL;
	pVal = GetDirectoryObjectAttrib(pDirObject,L"canonicalName");
 
	if (pVal)
	{
		// Parse the canonical name for the DNS name of the domain
		pFound = wcschr(pVal,'/');
		// Bind to the domain using the dns name, get defaultnamingcontext, 
		if (pFound)
		{
			iLen = pFound - pVal;
			wcscpy(pDomainPath, L"LDAP://");
			wcsncat(pDomainPath, pVal,iLen);
			wcscat(pDomainPath, L"/rootDSE");
			wprintf(L"DNS Name: %s\n", pDomainPath);
			VariantClear(&VarTest);
			hr = ADsOpenObject(pDomainPath,
							NULL,
							NULL,
							ADS_SECURE_AUTHENTICATION, // Use Secure Authentication
							IID_IADs,
							(void**)&pDomain);
			if (SUCCEEDED(hr))
			{
				hr = pDomain->Get(CComBSTR("defaultNamingContext"), &VarTest);
				if (SUCCEEDED(hr))
				{
					wcscpy(pDomainPath, L"LDAP://");
					 wcsncat(pDomainPath, pVal,iLen);
					wcscat(pDomainPath, L"/");
					wcscat(pDomainPath, VarTest.bstrVal);
					VariantClear(&VarTest);
					if (pDomain)
						pDomain->Release();
					if (SUCCEEDED(hr))
					{
						hr = ADsOpenObject(pDomainPath,
										NULL,
										NULL,
										ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
										IID_IADs,
										(void**)&pDomain);
						if (SUCCEEDED(hr))
						{
							hr = GetDomainMode(pDomain, bIsMixed);
					}
				}
			}
		}
			if (pDomain)
				pDomain->Release();
	}
}
	return hr;
}
 
 
////////////////////////////////////////////////////////////////////////////////////////////////////
/*
	GetDirectoryObjectAttrib()	-   Returns the value of the attribute named in pAttrName
										from the IDirectoryObject passed.
	Parameters
 
		IDirectoryObject *pDirObject	- Object from which to retrieve an attribute value
		LPWSTR pAttrName				- Name of attribute to retrieve
*/
WCHAR * GetDirectoryObjectAttrib(IDirectoryObject *pDirObject,LPWSTR pAttrName)
{
	HRESULT   hr;
	ADS_ATTR_INFO   *pAttrInfo=NULL;
	DWORD   dwReturn;
	static WCHAR pwReturn[1024];
 
	pwReturn[0] = 0l;
 
	hr = pDirObject->GetObjectAttributes( &pAttrName, 
											1, 
											&pAttrInfo, 
											&dwReturn ); 
	if ( SUCCEEDED(hr) )
	{
		for(DWORD idx=0; idx < dwReturn;idx++, pAttrInfo++ )   
		{
			if ( (_wcsicmp(pAttrInfo->pszAttrName,pAttrName) == 0 ) &&
				(pAttrInfo->dwADsType == ADSTYPE_CASE_IGNORE_STRING))
			{
				wcscpy(pwReturn,pAttrInfo->pADsValues->CaseIgnoreString);
				break;
		}
	}
		FreeADsMem( pAttrInfo );
}
	return pwReturn;
}

The following code example creates a group with only the essential properties explicitly set (cn, sAMAccountType, groupType) and containing no members.

'////////////////////////////////////////////////////////////////////////////////////////////////////
'	CreateSimpleGroup()   - Function for creating a basic group
'
'	Parameters
'
'		oDirObject As IDirectoryObject   -   Parent Directory Object for the new group
'		ByVal sCommonName As String	-   Common Name for the new group
'		ByVal sSAMAcctName As String	 -   Pointer to the Pointer which will receive the new Group
'		oDirObject As IDirectoryObject	- Parent Directory Object for the new group
'		ByVal sCommonName As String		 - Common Name for the new group
'		ByVal sSAMAcctName As String		- Sam Account Name for the new group
'		oDirObjectRet As IDirectoryObject   - New object returned
'		ByVal iGroupType As Long			- Bitflags for new group:
'																	ADS_GROUP_TYPE_GLOBAL_GROUP,
'																	ADS_GROUP_TYPE_DOMAIN_LOCAL_GROUP,
'																	ADS_GROUP_TYPE_UNIVERSAL_GROUP,
'																	ADS_GROUP_TYPE_SECURITY_ENABLED
Sub CreateSimpleGroup(oDirObject As IDirectoryObject, ByVal sCommonName As String, ByVal sSAMAcctName As String, oDirObjectRet As IDirectoryObject, ByVal iGroupType As Long)

	On Error GoTo CleanUp

	If Len(sSAMAcctName) > 20 Then
		MsgBox "SamAccountName CANNOT be bigger than 20 characters"
		Exit Sub
	End If
 
	Dim sGroupType As String
	Dim oNewObject As IADs
	Dim oIadsContDirObj As IADsContainer
	Set oIadsContDirObj = oDirObject
 
	' Get the string value for the group type.
	sGroupType = Str(iGroupType)

	' Get a New group object.
	Set oNewObject = oIadsContDirObj.Create("group", "CN=" & sCommonName)
 
	' Put the required attributes.
	oNewObject.Put "sAMAccountName", sSAMAcctName
	oNewObject.Put "GroupType", iGroupType
 
	' Commit the new group.
	oNewObject.SetInfo
 
	' Print group vitals.
	DisplayMessage ">>> Created new GROUP with a groupeType of " & Str(iGroupType)
	PrintIADSObject oNewObject
 
	Set oDirObjectRet = oNewObject
	Set oNewObject = Nothing
	Set oIadsContDirObj = Nothing
	Exit Sub

CleanUp:
	Set oNewObject = Nothing
	Set oIadsContDirObj = Nothing
	Set oDirObjectRet = Nothing

End Sub