Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for Creating a Group

[C++]

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;
}
[Visual Basic]

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