Platform SDK: Active Directory, ADSI, and Directory Services

Registering the SPNs for a Service

The following code fragment registers or unregisters one or more service principal names (SPNs) for an instance of a service.

The example calls the DsWriteAccountSpn function, which stores the SPNs in Microsoft Active Directory™ under the servicePrincipalName attribute of the account object specified by the pszServiceAcctDN parameter. The account object corresponds to the logon account specified in the CreateService call for this service instance. If the logon account is a domain user account, pszServiceAcctDN must be the distinguished name of the account object in Active Directory for that user account. If the service's logon account is the LocalSystem account, pszServiceAcctDN must be the distinguished name of the computer account object for the host computer on which the service is installed. For sample code that shows how to convert a domain\account format name to a distinguished name, see Example Code to Convert a SAM Name to a Distinguished Name.

// SpnRegister
// Register or unregister the SPNs under the service's account.
//
// The pszServiceAcctDN parameter is the distinguished name of the
// logon account for this instance of the service. 
//
// If the service runs in LocalSystem account, pszServiceAcctDN is the 
// distinguished name of the local computer account.
DWORD
SpnRegister(
    TCHAR *pszServiceAcctDN,    // DN of the service's logon account
    TCHAR **pspn,               // Array of SPNs to register
    unsigned long ulSpn,        // Number of SPNs in array
    DS_SPN_WRITE_OP Operation)  // Add, replace, or delete SPNs
{
 
DWORD dwStatus;
HANDLE hDs;
TCHAR szSamName[512];
DWORD dwSize = sizeof(szSamName);
WCHAR *pWhack = NULL;
PDOMAIN_CONTROLLER_INFO pDcInfo;

_tprintf(TEXT("SPN is:%s\n"), pspn[0]);
if (Operation == DS_SPN_ADD_SPN_OP)
    _tprintf(TEXT("SPN will be set for %s\n"), pszServiceAcctDN);
else
    _tprintf(TEXT("SPN will be removed from %s\n"), pszServiceAcctDN);

// Bind to a domain controller. 
// Get the domain for the current user.
if ( GetUserNameEx( NameSamCompatible, szSamName, &dwSize ) )    
{
    pWhack = wcschr( szSamName, L'\\' );
    if ( pWhack )
        *pWhack = L'\0';
} else 
{
    _tprintf(TEXT("GetUserNameEx failed - %d\n"), GetLastError());
    return GetLastError() ;
}
 
// Get the name of a domain controller in that domain.
dwStatus = DsGetDcName(
                    NULL,
                    szSamName,
                    NULL,
                    NULL,
                    DS_IS_FLAT_NAME |
                    DS_RETURN_DNS_NAME |
                    DS_DIRECTORY_SERVICE_REQUIRED,
                    &pDcInfo );
if ( dwStatus != 0 ) 
{
    _tprintf(TEXT("DsGetDcName failed - %d\n"), dwStatus);
    return dwStatus;
}
 
// Bind to the domain controller.
dwStatus = DsBind( pDcInfo->DomainControllerName, NULL, &hDs );
 
// Free the DOMAIN_CONTROLLER_INFO buffer.
NetApiBufferFree( pDcInfo );
if ( dwStatus != 0 ) 
{
    _tprintf(TEXT("DsBind failed - %d\n"), dwStatus);
    return dwStatus;
}
 
// Write the SPNs to the service account or computer account.
dwStatus = DsWriteAccountSpn(
        hDs,         // handle to the directory
        Operation,   // Add or remove SPN from account's existing SPNs
        pszServiceAcctDN,        // DN of service account or computer account
        ulSpn,                   // Number of SPNs to add
        (const TCHAR **)pspn);   // Array of SPNs
if (dwStatus != NO_ERROR) 
    _tprintf(TEXT("Failed to write SPN: Error was %X\n"),dwStatus);
 
// Unbind the DS in any case.
DsUnBind(&hDs);
 
return(dwStatus);
}