Platform SDK: Active Directory, ADSI, and Directory Services |
The following code fragment 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; //Check if the group type is Universal Security //if true, make sure 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)) { //Check whether the domain containing the container is in mixed mode hr = CheckDomainModeOfObject(pDirObject, &bIsMixed); if (SUCCEEDED(hr)) { if (bIsMixed) return E_INVALIDARG; } else { return hr; } } //SamAccountName CANNOT be bigger than 20 characters. if (wcslen(pwSamAcctName) >20) { return E_FAIL; } HRESULT hr; /* ADSVALUE is used to specify attribute information 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 will be 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[1024]; // Build an Array of ADS_ATTR_INFO structures // Note 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; //Note 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 LPOLESTR szAttribute = L"ntMixedDomain"; hr = pDomain->Get(szAttribute,&var); if (SUCCEEDED(hr)) { //Type should be VT_I4. if (var.vt==VT_I4) { //Zero means native mode. if (var.lVal == 0) *bIsMixed = FALSE; //One means 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]; //Check whether the domain containing 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(L"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 ) { wcscpy(pwReturn,pAttrInfo->pADsValues->CaseIgnoreString); break; } } FreeADsMem( pAttrInfo ); } return pwReturn; }
The following subroutine 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_ENABLE Sub CreateSimpleGroup(oDirObject As IDirectoryObject, ByVal sCommonName As String, ByVal sSAMAcctName As String, oDirObjectRet As IDirectoryObject, ByVal iGroupType As Long) 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 End Sub