Developing with Adaxes .NET API

Adaxes exposes a set of ADSI classes and interfaces that you can use to interact with the Adaxes service. This enables you to build custom applications and integrations for Adaxes, using .NET programming languages.

This article introduces you to the base principles of developing .NET applications and external PowerShell scripts for Adaxes. That is, scripts which run in standalone PowerShell or within third-party applications. For information about writing internal PowerShell scripts to use in business rules, custom commands, and so on, see Server-side scripting.

Adaxes also has a REST API. Its functionality is limited to managing directory objects, but it is language-independent. As long as the programming or scripting language of your choice can send HTTP requests, you can use the REST API. For details, see the REST API overview.

Importing the Adaxes library

 In PowerShell scripts

To get access to Adaxes classes and interfaces, import the Adaxes PowerShell module. The module has to be installed.

Import-Module Adaxes

This approach works in both, PowerShell 5.1 and PowerShell 7.

 In .NET projects

Adaxes client library is available on NuGet as a package named Softerra.Adaxes.Client. To get access to Adaxes classes and interfaces, reference this package in your project.

The library can be used in projects that target:

  • .NET Framework 4.8
  • .NET 8.0 and above

If your project targets .NET, it also has to target the Windows operating system. Make sure to include -windows in TargetFramework in your project file. For example:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0-windows</TargetFramework>
  </PropertyGroup>
</Project>
<ItemGroup>
  <PackageReference Include="Softerra.Adaxes.Client" Version="3.18.0" />
</ItemGroup>

Connecting to an Adaxes service

To connect to an Adaxes service instance running on a specific host:

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");
    }
}

A successful connection is represented by the IAdmService interface.

 Connecting to a nearest available service

If you have multiple Adaxes services sharing configuration, you can let Adaxes automatically pick the nearest available instance instead of specifying which instance to connect to.

To connect to the nearest available Adaxes service from a specific configuration set:

PowerShell
Import-Module Adaxes

# Only for example purposes. Never store credentials in plaintext.
$username = "admin@example.com"
$password = "MyPassword!"

# Connect to an Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetNearestService("mydomain.com", $username, $password)
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {

        // Only for example purposes. Never store credentials in plaintext.
        string username = "admin@example.com";
        string password = "MyPassword!";

        // Connect to an Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetNearestService("mydomain.com", username, password);
    }
}

Binding to objects

To perform any operation with an object, such as a user account, you need to bind to it. The binding method is identical for directory objects and Adaxes configuration objects – IAdmService.OpenObject.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$user = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", $null, $null, 0)
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADsUser user = (IADsUser)service.OpenObject(
            "Adaxes://CN=John Smith,CN=Users,DC=company,DC=com",
            null, 
            null, 
            0);
    }
}

After binding to an object, you can use the appropriate interfaces to perform operations on the object. The interfaces you can use depend on the type of the object.

  • All object types support the IADs and IAdmTop interfaces.
  • User objects support the IADsUser interface.
  • Group objects support the IADsGroup interface.
  • Container objects, such as organizational units, support the IADsContainer interface.

For a full list, see Interfaces supported by objects.

Binding by specific identifiers

To bind to an object, you need to know its ADS path, which can be constructed from various object identifiers. Adaxes uses an identical ADS path structure for directory objects and Adaxes-specific objects.

"Adaxes://Server:Port/ObjectIdentifier"
  • Adaxes – the case sensitive namespace name.

  • Server (optional) – the domain controller name, IP address, or the domain name where the Adaxes will search for the directory object referred by the ADS path.

  • Port (optional) – the port to be used for the connection. If no port number is specified, the default port number will be used. The default port number is 389 if not using an SSL connection or 636 if using an SSL connection.

  • ObjectIdentifier – the identifier of an object. The identifier can be a distinguished name (DN), GUID, or SID.

     Binding to an object by DN

    To bind to a directory object using the object's distinguished name, the ObjectIdentifier part of the ADS path must contain the DN of the object. For example:

    Adaxes://CN=John Smith,CN=Users,DC=mycompany,DC=com
    

    or

    Adaxes://mydomain.com/CN=John Smith,CN=Users,DC=mycompany,DC=com
    

    For details on how to get the DN of a directory object, see Get the DN of a directory object.

    DNs of Microsoft Entra objects

    The distinguished names of Microsoft Entra objects contain the object GUID to make the DN unique, because Microsoft Entra ID allows creating multiple objects with the same name. For example:

    CN=John Smith\0AUID:952322ad597f4b1bb4ce40360a1bc07c,OU=Users,DC=example,DC=onmicrosoft,DC=com
    
    CN=John Smith\0AUID:86ecfb68226f43e295fe50a370b9db31,OU=Users,DC=example,DC=onmicrosoft,DC=com
    

    When you specify a DN to bind to a Microsoft Entra object, you can omit the GUID:

    CN=John Smith,OU=Users,DC=example,DC=onmicrosoft,DC=com
    

    However, if multiple objects in Microsoft Entra ID have the same name (e.g. two users named John Smith) and are located in the same container in Adaxes, Adaxes will bind to the first object it encounters.

    Example

    The following code sample binds to a specific user account by DN and then disables the account.

    PowerShell
    Import-Module Adaxes
    
    # Connect to the Adaxes service.
    $ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
    $service = $ns.GetServiceDirectly("localhost")
    
    $user = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=mycompany,DC=com", $null, $null, 0)
    $user.AccountDisabled = $true
    $user.SetInfo()
    
    C#
    using Softerra.Adaxes.Adsi;
    using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
    
    class Program
    {
        static void Main(string[] args)
        {
            // Connect to the Adaxes service.
            AdmNamespace ns = new AdmNamespace();
            IAdmService service = ns.GetServiceDirectly("localhost");
    
            IADsUser user = (IADsUser)service.OpenObject(
                "Adaxes://CN=John Smith,CN=Users,DC=mycompany,DC=com", null, null, 0);
            user.AccountDisabled = true;
            user.SetInfo();
        }
    }
    
     Binding to an object by GUID

    Each object has a globally unique identifier (GUID) that is stored in the objectGUID property. As opposed to the distinguished name that changes when an object is renamed or moved, the GUID never changes.

    If you want an ADS path to be valid after an object is renamed or moved, you need to specify the object's GUID as the ObjectIdentifier part. For example:

    Adaxes://<GUID=90495758-7E98-47B6-AA98-5B49129EF1DB>
    

    If your Adaxes service manages multiple domains, it is recommended that the ADS path contains the name of the domain where the object is located. This will make the binding operation faster.

    Adaxes://mydomain.com/<GUID=90495758-7E98-47B6-AA98-5B49129EF1DB>
    

    Example

    The following code sample binds to a specific directory object by its DN, gets the GUID of the object, and then binds to the object using the GUID.

    PowerShell
    Import-Module Adaxes
    
    # Connect to the Adaxes service.
    $ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
    $service = $ns.GetServiceDirectly("localhost")
    
    $object = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", $null, $null, 0)
    $guid = [Guid]$object.Get("objectGUID")
    $guidPath = "Adaxes://<GUID=$guid>"
    $object = $service.OpenObject($guidPath, $null, $null, 0)
    
    C#
    using System;
    using Softerra.Adaxes.Adsi;
    using Softerra.Adaxes.Interop.Adsi;
    using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
    
    class Program
    {
        static void Main(string[] args)
        {
            // Connect to the Adaxes service.
            AdmNamespace ns = new AdmNamespace();
            IAdmService service = ns.GetServiceDirectly("localhost");
    
            IADs obj = (IADs)service.OpenObject(
                "Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", null, null, 0);
            byte[] guidBytes = (byte[])obj.Get("objectGUID");
            Guid guid = new Guid(guidBytes);
            string guidPath = string.Format("Adaxes://<GUID={0}>", guid);
            obj = (IADs)service.OpenObject(guidPath, null, null, 0);
        }
    }
    
     Binding to an object by SID

    Each security principal (user, security group, computer) has a security identifier (SID) stored in the objectSID property. The SID is unique within the domain and does not change even if the object is renamed or moved to another container (within the same domain).

    To bind to an object by its SID, set the ObjectIdentifier part of the ADS path as the SID of the object in the SDDL format. For example:

    Adaxes://<SID=S-1-5-21-573937-2149998-410785>
    

    If your Adaxes service manages multiple domains, it is recommended that the ADS path contains the name of the domain where the object is located. This will make the binding operation faster.

    Adaxes://mydomain.com/<SID=S-1-5-21-573937-2149998-410785>
    

    To convert the value of the objectSID property to the SDDL format, you can use the Sid class.

    Example

    The following code sample binds to a specific user by its DN, gets the SID of the user, and then binds to the user using the SID.

    PowerShell
    Import-Module Adaxes
    
    # Connect to the Adaxes service.
    $ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
    $service = $ns.GetServiceDirectly("localhost")
    
    $user = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", $null, $null, 0)
    $sidBytes = $user.Get("objectSID")
    $sid = New-Object "Softerra.Adaxes.Adsi.Sid" @($sidBytes, 0)
    $sidPath = "Adaxes://<SID=$sid>"
    $user = $service.OpenObject($sidPath, $null, $null, 0)
    
    C#
    using Softerra.Adaxes.Adsi;
    using Softerra.Adaxes.Interop.Adsi;
    using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
    
    class Program
    {
        static void Main(string[] args)
        {
            // Connect to the Adaxes service.
            AdmNamespace ns = new AdmNamespace();
            IAdmService service = ns.GetServiceDirectly("localhost");
    
            IADs user = (IADs)service.OpenObject(
                "Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", null, null, 0);
            byte[] sidBytes = (byte[])user.Get("objectSID");
            Sid sid = new Sid(sidBytes, 0);
            string sidPath = string.Format("Adaxes://<SID={0}>", sid);
            user = (IADs)service.OpenObject(sidPath, null, null, 0);
        }
    }
    

    To manipulate ADS paths, you can use the AdsPath and DN helper classes.

Binding to well-known objects

 Binding to Adaxes configuration objects {id="bind-to-adaxes-objects"}

You can bind to and manage Adaxes configuration objects similarly to how you would manage directory objects.

Each type of an Adaxes configuration object is stored in a dedicated container. There is a separate container for business rules, security roles, mail settings, web interface configurations, and so on. Each container has a user-friendly alias which you can use to obtain its ADS path. For a full list, see Aliases for containers that store Adaxes configuration objects.

The following code sample gets the ADS path of the container for business rules by its alias, and binds to it.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$businessRulesPath = $service.Backend.GetConfigurationContainerPath("BusinessRules")
$container = $service.OpenObject($businessRulesPath, $null, $null, 0)
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        string businessRulesPath = service.Backend.GetConfigurationContainerPath("BusinessRules");
        IADsContainer container = (IADsContainer)service.OpenObject(
            businessRulesPath, null, null, 0);
    }
}

Managing Adaxes configuration

For further information on managing Adaxes configuration objects, see the following articles:

Aliases for containers that store Adaxes configuration objects

Container Alias
Managed domains ManagedDomains
Security roles AccessControlRoles
Business rules BusinessRules
Property patterns PropertyPatterns
Custom commands CustomCommands
Scheduled tasks ScheduledTasks
Approval requests ApprovalRequests
Business units BusinessUnits
Policies for password self-service PasswordSelfServicePolicies
Microsoft 365 tenants CloudServicesO365
Root container for reports ReportsRoot
Reports \ Overview ReportOverviews
Reports \ All Reports Reports
Reports \ Schedule ReportSchedule
Report categories ReportCategories
SMS settings SmsSettings
Mail settings MailSettings
Service settings ServiceSettings
Common settings for Adaxes services that share common configuration ConfigurationSetSettings
Web interface configurations WebUIConfigurationContainer
Client applications ClientAppsContainer
 Binding to the parent object

To get the ADS path of the container where a particular object resides, you can use the IADs.Parent property. Every directory object implements the IADs interface.

Example

The following code sample binds to the parent container of a directory object.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$object = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=mycompany,DC=com", $null, $null, 0)

$parent = $service.OpenObject($object.Parent, $null, $null, 0)
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADs obj = (IADs)service.OpenObject(
            "Adaxes://CN=John Smith,CN=Users,DC=mycompany,DC=com", null, null, 0);

        IADs parent = (IADs)service.OpenObject(obj.Parent, null, null, 0);
    }
}
 Binding to child objects

To bind to any child object in a container, you can use the IADsContainer.GetObject method. Every container object implements the IADsContainer interface.

Example

The following binds to a user named John Smith in the Users container.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$parent = $service.OpenObject("Adaxes://CN=Users,DC=mycompany,DC=com", $null, $null, 0)

$child = $parent.GetObject("user", "CN=John Smith")
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADsContainer parent = (IADsContainer)service.OpenObject(
            "Adaxes://CN=Users,DC=mycompany,DC=com", null, null, 0);

        IADs child = (IADs)parent.GetObject("user", "CN=John Smith");
    }
}

Alternatively, you can use the AdsPath helper class to build the ADS path of a child object like in the example below. This approach works only if the ADS path of the parent object is constructed using a distinguished name (DN).

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$parentPath = "Adaxes://CN=Users,DC=mycompany,DC=com"
$adsPath = New-Object "Softerra.Adaxes.Adsi.AdsPath" $parentPath

$childPath = $adsPath.CreateChildPath("CN=John Smith")
$child = $service.OpenObject($childPath.ToString(), $null, $null, 0)
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        const string parentPath = "Adaxes://CN=Users,DC=mycompany,DC=com";
        AdsPath adsPath = new AdsPath(parentPath);

        AdsPath childPath = adsPath.CreateChildPath("CN=John Smith");
        IADs child = (IADs)service.OpenObject(childPath.ToString(), null, null, 0);
    }
}
 Binding to the RootDSE

RootDSE (Root Directory Service Entry) is the root of the directory data tree. Properties of the RootDSE object can be used to retrieve data such as distinguished names of the domain, schema, and configuration containers. The following ADS path can be used to bind to the RootDSE:

Adaxes://<domain>/rootDSE

The <domain> is the name of a domain or the DNS name of a domain controller. The <domain> is optional. The following path is totally valid:

Adaxes://rootDSE

In this case, the path will refer to the RootDSE of the AD LDS instance used to store Adaxes configuration.

Example

The following code sample binds to the RootDSE of a domain and gets the DNS name of the domain controller being used by the Adaxes service.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$rootDse = $service.OpenObject("Adaxes://mydomain.com/rootDSE", $null, $null, 0)
$dcDnsName = $rootDse.Get("dnsHostName")

Write-Host $dcDnsName
C#
using System;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADs rootDse = (IADs)service.OpenObject(
                "Adaxes://mydomain.com/rootDSE", null, null, 0);
        string dcDnsName = (string)rootDse.Get("dnsHostName");

        Console.WriteLine(dcDnsName);
    }
}
 Binding to the domain partition {id=bind-to-domain-partition}

The domain partition (or the default naming context) stores directory objects like users, groups, computers and organizational units. The domain partition is replicated to all domain controllers of a domain. When you bind to the domain partition, you bind to the top container of the domain. If you want to search in the entire domain, you need to perform a search under the domain partition object.

The following ADS path can be used to bind to the domain partition of a domain:

Adaxes://<servername>

The <servername> is the name of a domain or the DNS name of a domain controller.

Example

The following code sample updates the minimum password length in the Default Domain Password Policy by updating the minPwdLength property of the domain partition object.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$defaultNC = $service.OpenObject("Adaxes://mydomain.com", $null, $null, 0)
$defaultNC.Put("minPwdLength", 8)
$defaultNC.SetInfo()
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADs defaultNC = (IADs)service.OpenObject(
            "Adaxes://mydomain.com", null, null, 0);
        defaultNC.Put("minPwdLength", 8);
        defaultNC.SetInfo();
    }
}

Searching for objects

You can search for directory objects and Adaxes configuration objects, such as security roles, business rules, and scheduled tasks. To perform a search query, you need to bind to the container in which you want to search. All containers support the IAdmDirectorySearcher interface. Use the Criteria and SearchScope properties to define the search criteria and scope.

To start searching, call IAdmDirectorySearcher.ExecuteSearch. This method returns a IAdmSearchResultIterator interface that you can use to iterate through search results.

To fetch all search results, call IAdmSearchResultIterator.FetchAll. Each search result is represented by IAdmSearchResult.

The following code sample searches for all users from the Sales department in the organizational unit called People.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$searcher = $service.OpenObject(
    "Adaxes://OU=People,DC=company,DC=com", $null, $null, 0)

$searcher.Criteria = New-AdmCriteria "user" {department -eq "Sales"}
$searcher.SearchScope = "ADS_SCOPE_SUBTREE"
try
{
    $results = $searcher.ExecuteSearch()
    foreach ($result in $results.FetchAll())
    {
        Write-Host $result.ADsPath
    }
}
finally
{
    $results.Dispose()
}
C#
using System;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
using Softerra.Adaxes.Directory.Criteria;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IAdmDirectorySearcher searcher = (IAdmDirectorySearcher)service.OpenObject(
            "Adaxes://OU=People,DC=company,DC=com", null, null, 0);

        // Build search criteria.
        SimpleCriteriaItem userCriteria = new()
        {
            Property = "department",
            Operator = "eq",
            Values = { "Sales" }
        };
        Criteria criteria = new();
        criteria.AddType("user", userCriteria);

        // Set search parameters and execute search.
        searcher.Criteria = criteria;
        searcher.SearchScope = ADS_SCOPEENUM.ADS_SCOPE_SUBTREE;

        using (IAdmSearchResultIterator results = searcher.ExecuteSearch())
        {
            foreach (IAdmSearchResult result in results.FetchAll())
            {
                Console.WriteLine(result.AdsPath);
            }
        }
    }
}

See also:

Reading object properties

To read the properties of a directory object, use the Get or GetEx methods of the IADs interface, supported by all objects. All Adaxes-specific properties have the adm- prefix and can be retrieved like any other property.

The following code sample gets the username of a user account.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$user = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", $null, $null, 0)
$username = $user.Get("userPrincipalName")
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADs user = (IADs)service.OpenObject(
            "Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", null, null, 0);
        string username = (string)user.Get("userPrincipalName");
    }
}

Certain properties are accessed through dedicated interfaces instead of IADs. For example, to get the Terminal Services profile path of a user, read the TerminalServicesProfilePath property of the IADsTSUserEx interface.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$user = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", $null, $null, 0)
$tsProfilePath = $user.TerminalServicesProfilePath
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADsTSUserEx tsUser = (IADsTSUserEx)service.OpenObject(
            "Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", null, null, 0);
        string tsProfilePath = tsUser.TerminalServicesProfilePath;
    }
}

For more information, see Interfaces supported by directory objects.

Creating objects

To create a new directory object, you need to bind to the organizational unit or container where you want to create the object.

After binding, call IADsContainer.Create and pass the object class and relative distinguished name (RDN) of the new object to the method. Use the same RDN structure when creating Active Directory and Entra ID objects.

The method returns an instance of an ADSI object which represents the new directory object. To save it to the directory, set its properties using IADs.Put or IADs.PutEx, and then call IADs.SetInfo.

The following code sample creates a new user in the Users container.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

# Bind to the parent container
$parent = $service.OpenObject("Adaxes://CN=Users,DC=company,DC=com", $null, $null, 0)

# Create a new user object
$user = $parent.Create("user", "CN=John Smith")

# Set object properties
$user.Put("givenName", "John") # First name
$user.Put("sn", "Smith") # Last name
$user.Put("userPrincipalName", "jsmith") # Username
$user.Put("unicodePwd", "secret") # Password
$user.Put("pwdLastSet", 0) # Must change password at next logon
$user.AccountDisabled = $false

# Save the user to the directory
$user.SetInfo()
C#
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        // Bind to the parent container.
        IADsContainer parent = (IADsContainer)service.OpenObject(
            "Adaxes://CN=Users,DC=company,DC=com", null, null, 0);

        // Create a new user object.
        IADs user = (IADs)parent.Create("user", "CN=John Smith");

        // Set object properties.
        user.Put("givenName", "John"); // First name
        user.Put("sn", "Smith"); // Last name
        user.Put("userPrincipalName", "jsmith"); // username
        user.Put("unicodePwd", "secret"); // Password
        user.Put("pwdLastSet", 0); // Must change password at next logon
        ((IADsUser)user).AccountDisabled = false;

        // Save the user to the directory.
        user.SetInfo();
    }
}

See also:

Modifying objects

To modify the properties of an object, use either IADs.Put or IADs.PutEx. Both methods make changes to property values in the property cache, which means they won't persist unless you explicitly save them. To save the changes to the directory, call IADs.SetInfo.

The following code sample modifies the description and the account expiration date of a user object.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$user = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", $null, $null, 0)

$user.Put("description", "My description")
$expirationDate = (Get-Date).AddDays(30) # Current date + 30 days
$user.Put("accountExpires", $expirationDate)
$user.SetInfo()
C#
using System;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADs user = (IADs)service.OpenObject(
            "Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", null, null, 0);

        user.Put("description", "My description");
        DateTime expirationDate = DateTime.Now.AddDays(30); // current date + 30 days
        user.Put("accountExpires", expirationDate);
        user.SetInfo();
    }
}

See also:

Copying objects

To copy a directory object, bind to the container or organizational unit where the copy should be placed.

After binding to the container, call IADsContainer.CopyHere and pass the ADS path of the object you want to copy as the method parameter. If you copying an object to the same container, you must specify a different relative distinguished name (RDN) for the new object.

The following code sample creates a new user by copying an existing account.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

# Bind to the target container.
$targetContainer = $service.OpenObject("Adaxes://CN=Users,DC=company,DC=com", $null, $null, 0)

# Create a new user object by copying the user account of John Smith.
$sourceUserPath = "Adaxes://CN=John Smith,OU=Sales,DC=company,DC=com"
$newUserRdn = "CN=Ann Jones"
$user = $targetContainer.CopyHere($sourceUserPath, $newUserRdn)

# Update some properties.
$user.Put("givenName", "Ann") # First name
$user.Put("sn", "Jones") # Last name
$user.Put("userPrincipalName", "ajones") # Username
$user.Put("unicodePwd", "secret") # Password
$user.Put("pwdLastSet", 0); # Must change password at next logon

# Save the user to the directory.
$user.SetInfo()
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        // Bind to the target container
        IADsContainer targetContainer = (IADsContainer)service.OpenObject(
            "Adaxes://CN=Users,DC=company,DC=com", null, null, 0);

        // Create a new user object by copying the user account of John Smith.
        const string sourceUserPath = "Adaxes://CN=John Smith,OU=Sales,DC=company,DC=com";
        const string newUserRdn = "CN=Ann Jones";
        IADs user = (IADs)targetContainer.CopyHere(sourceUserPath, newUserRdn);

        // Update some properties.
        user.Put("givenName", "Ann"); // First name
        user.Put("sn", "Jones"); // Last name
        user.Put("userPrincipalName", "ajones"); // Username
        user.Put("unicodePwd", "secret"); // Password
        user.Put("pwdLastSet", 0); // Must change password at next logon

        // Save the user to the directory.
        user.SetInfo();
    }
}

See also:

Moving objects

To move a directory object from one location to another, bind to the destination container or organizational unit.

After binding to the container, call IADsContainer.MoveHere and pass the ADS path of the object you want to move as the method parameter.

The following code sample moves a user from one organizational unit to another.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

# Bind to the target organizational unit.
$targetOU = $service.OpenObject("Adaxes://CN=TargetOU,DC=company,DC=com", $null, $null, 0)

# Move the account of John Smith from SourceOU to TargetOU.
$userPath = "Adaxes://CN=John Smith,OU=SourceOU,DC=company,DC=com"
$movedUser = $targetOU.MoveHere($userPath, $null)
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        // Bind to the target organizational unit.
        IADsContainer targetOU = (IADsContainer)service.OpenObject(
            "Adaxes://CN=TargetOU,DC=company,DC=com", null, null, 0);

        // Move the account of John Smith from SourceOU to TargetOU.
        const string userPath = "Adaxes://CN=John Smith,OU=SourceOU,DC=company,DC=com";
        IADsUser movedUser = (IADsUser)targetOU.MoveHere(userPath, null);
    }
}

Deleting objects

To delete a directory object, bind to it and call IADsDeleteOps.DeleteObject. The IADsDeleteOps interface is supported by all directory objects.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$user = $service.OpenObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", $null, $null, 0)

$user.DeleteObject("ADM_DELETEOBJECTFLAGS_AUTO")
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.Utils;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADsDeleteOps user = (IADsDeleteOps)service.OpenObject(
            "Adaxes://CN=John Smith,CN=Users,DC=company,DC=com", null, null, 0);

        user.DeleteObject(ADM_DELETEOBJECTFLAGS_ENUM.ADM_DELETEOBJECTFLAGS_AUTO);
    }
}

Deleted objects can be restored. For details, see Restoring deleted objects.

Reading the execution log

When performing operations on directory objects, Adaxes may execute additional actions. For example, you may have a business rule that automatically assigns Microsoft 365 licenses to newly created users.

The information about additional actions is recorded in the execution log of an operation. To get the details of the last operation performed on a directory object, you can use the IAdmLastOperationOps interface supported by all directory objects. The IAdmLastOperationOps.GetLastOperationInfo method returns the IAdmOperationInfo interface, which you can use to obtain various information about the operation.

To get the operation execution log, use the IAdmOperationInfo.ExecutionLog property. The execution log is represented by the IAdmExecutionLogEntryCollection interface. Execution log records are represented by the IAdmExecutionLogEntry interface.

Example

The following code sample outputs the execution log of a user creation operation.

PowerShell
Import-Module Adaxes

# Connect to the Adaxes service.
$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$service = $ns.GetServiceDirectly("localhost")

$parent = $service.OpenObject("Adaxes://CN=Users,DC=company,DC=com",
    $null, $null, 0)
$user = $parent.Create("user", "CN=John Smith")
$user.Put("userPrincipalName", "jsmith")
$user.SetInfo()

$operationInfo = $user.GetLastOperationInfo()
$executionLog = $operationInfo.ExecutionLog

Write-Host $executionLog.ToString()
C#
using System;
using Interop;
using Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;

class Program
{
    static void Main(string[] args)
    {
        // Connect to the Adaxes service.
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADsContainer parent = (IADsContainer)service.OpenObject(
            "Adaxes://CN=Users,DC=company,DC=com", null, null, 0);

        IADs user = (IADs)parent.Create("user", "CN=John Smith");
        user.Put("userPrincipalName", "jsmith");
        user.SetInfo();

        IAdmOperationInfo operationInfo =
            ((IAdmLastOperationOps)user).GetLastOperationInfo();
        IAdmExecutionLogEntryCollection executionLog = operationInfo.ExecutionLog;

    Console.Write(executionLog.ToString());
    }
}

For more ways of accessing log records, see Accessing log records.

See also