Writing ADSI scripts

Adaxes ADSI Provider lets you use ADSI interfaces to connect and communicate with Adaxes Service. You can use the ADSI Provider in custom client applications, standalone scripts, and scripts executed by business rules, scheduled tasks and custom commands.

The Adaxes ADSI objects are COM Automation objects that can be accessed and manipulated by any language that supports COM, such as VBScript, PowerShell, C++, and .NET languages like VB.NET and C#.

Referencing ADSI Provider assembly

Adaxes ADSI Provider is implemented as a .NET assembly targeted for Microsoft .NET Framework 4.8. The assembly is called Softerra.Adaxes.Adsi.dll and is installed into the GAC when you install Adaxes ADSI Provider .

 Referencing the assembly in PowerShell scripts

To use the ADSI Provider in a PowerShell script, you need to load the Softerra.Adaxes.Adsi assembly into the PowerShell session:

[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

If your PowerShell script is executed by a business rule, scheduled task, or custom command, the assembly is loaded automatically.

 Adding a reference to the assembly in a .NET project
  1. Ensure that the target framework for the project is .NET Framework 4.8.

  2. Add a reference to the Softerra.Adaxes.Adsi assembly to the project (the assembly is installed into the GAC).

    Softerra.Adaxes.Adsi

Connecting to Adaxes service

To start using the ADSI Provider, you need to establish a connection with an Adaxes service. You can either establish a connection with the specific Adaxes service running on a specific computer, or, if you have multiple Adaxes services sharing configuration, you can let the provider select the service to connect to. In the latter case, the ADSI provider will connect to the nearest available Adaxes service.

Connecting to a specific service

To connect to the Adaxes service running on a given computer, you need to create an instance of the AdmNamespace class and call the GetServiceDirectly method, passing the DNS name or IP address of the computer as the parameter of the method. The AdmNamespace class supports the IAdmNamespace interface. A connection is represented by the IAdmService interface.

Poweshell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");
    }
}
 Connecting to the nearest available service

All Adaxes services sharing common configuration are grouped into logical units called configuration sets. Adaxes ADSI Provider allows you to connect to the nearest available Adaxes service that belongs to a configuration set. To connect to the nearest Adaxes service from a configuration set, you need to create an instance of the AdmNamespace class and call the GetNearestService method. The first parameter of the method specifies the name of the Active Directory domain where to search for Adaxes services. The 2nd and 3rd parameters of the method are the username and password that will be used to access the domain. If you want to use the credentials of the currently logged on user, set the parameters to null. A connection is represented by the IAdmService interface.

Poweshell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$ns = New-Object "Softerra.Adaxes.Adsi.AdmNamespace"
$username = $null
$password = $null
$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)
    {
        AdmNamespace ns = new AdmNamespace();
        string username = null;
        string password = null;
        IAdmService service = ns.GetNearestService("mydomain.com", username, password);
    }
}

If there are multiple configuration sets in your domain, you need to specify which one to use by setting the ID of the desired configuration set to the DefaultConfigurationSet property of the AdmNamespace instance.

$ns.DefaultConfigurationSet = "{c91c481f-64e3-455c-a674-8b857e444fec}"

For information on how to get the ID of a configuration set, see Get the configuration set ID .

Binding to ADSI objects

To perform any operation in the directory, you need to bind to a directory object first. For example, to read and write properties of an object, you need to bind to the object. To create a new object, you need to bind to the OU where you want to create the object. To search the directory, you need to bind to the directory object, under which you want to perform a search. 'Directory object' here includes Active Directory, Microsoft Entra ID, and Adaxes configuration objects like business rules, security roles, scheduled tasks, and business units.

After you've bound to an object, you can use the appropriate ADSI interfaces to perform operations on the object. The interfaces you can use depend on the type of the directory 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 that allows you to enumerate and manage their child objects. For more information, see Interfaces supported by directory objects.

To bind to a directory object, you need to call the OpenObject method of the IAdmService interface. To specify the object to which you want to bind, you need to provide the ADS path of the object as the first parameter of the method.

Poweshell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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);
    }
}

If your script is executed by a business rule, scheduled task, or custom command, you can use the BindToObject method of the predefined variable $Context:

$adObject = $Context.BindToObject("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com")

The ADS path requires the following format:

 Adaxes://[Server[:Port]]/ObjectIdentifier
  • Adaxes (required): Specifies the namespace name of the ADSI provider. The namespace name for the Adaxes ADSI Provider is Adaxes. The namespace name of the provider is case sensitive.

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

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

  • ObjectIdentifier (required): Specifies the identifier of a directory object. The identifier can be a distinguished name (DN), GUID or SID (see examples below).

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

 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.


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

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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 directory object has a globally unique identifier (GUID) that is stored in the objectGUID property of the object. As opposed to the distinguished name that changes when an object is renamed or moved, the object GUID never changes. So, if you want an ADS path to be valid after an object is renamed or moved, you need to use the object's GUID in the path.

To bind to a directory object using the object's GUID, the ObjectIdentifier part of the ADS path must contain the GUID of the object. 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>

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
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        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) that is stored in the objectSID property of the object. 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).

Adaxes ADSI provider allows you to bind to a directory object using the object's SID. To bind to an object using its SID, the ObjectIdentifier part of the ADS path must contain the SID of the object in the SDDL form. 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 form, you can use the Sid class.

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
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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);
    }
}
 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, as shown in the following ADS path:

Adaxes://rootDSE

In this case, the path will refer to the RootDSE of the directory server that is used to store Adaxes configuration data (ADAM or AD/LDS).

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
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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. This means that if, for example, you want to search the whole 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.

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
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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();
    }
}
 Binding to an object's parent

In ADSI, every directory object is represented by an object that exposes the IADs interface. To get the ADS path of the parent container of an object, you can use the IADs::Parent property. Then you can use the obtained ADS path to bind to the parent object.

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

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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

In ADSI, every container object exposes the IADsContainer interface. You can use the IADsContainer::GetObject method to bind to a child object. The method takes the object class and the relative distinguished name (RDN) of the directory object you want to bind to as parameters.

The following binds to a child object of a container.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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");
    }
}

You can also use the AdsPath helper class to build the ADS path of a child object.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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);
    }
}

This approach works only if the ADS path of the parent object contains the object DN.

 Binding to Adaxes-specific objects

Using Adaxes ADSI Provider, you can also manage Adaxes configuration objects, such as security roles, business rules, scheduled tasks, and business units. Adaxes-specific objects are stored in the Adaxes Configuration Server (ADAM or AD/LDS) that is installed on the computer where the Adaxes Service is running. You can manage Adaxes configuration objects in the same way you manage directory objects.

Each type of an Adaxes configuration object is stored in a separate container. That is, there is a separate container for business rules, security roles, property patterns, etc.

To get the ADS path of a container that stores Adaxes configuration objects, you need to call the GetConfigurationContainerPath method of the IAdmServiceBackend interface and provide the alias of the desired container as the first parameter of the method. The IAdmServiceBackend interface can be obtained via the Backend property of the AdmNamespace instance.

The following code sample gets the ADS path of the container where business rules are stored.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

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

$businessRulesPath = $service.Backend.GetConfigurationContainerPath("BusinessRules")
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        string businessRulesPath = service.Backend.GetConfigurationContainerPath("BusinessRules");
    }
}

Aliases for containers that store Adaxes configuration objects

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

Example 1 – Bind to the Scheduled Tasks container.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

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

$scheduledTasksPath = $service.Backend.GetConfigurationContainerPath(`
    "ScheduledTasks")
$scheduledTasksContainer = $service.OpenObject($scheduledTasksPath,`
    $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)
    {
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        string scheduledTasksPath = service.Backend.GetConfigurationContainerPath(
            "ScheduledTasks");
        IADsContainer scheduledTasksContainer =
            (IADsContainer)service.OpenObject(scheduledTasksPath, null, null, 0);
    }
}

Example 2 – Bind to the scheduled task named My Task.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

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

$scheduledTasksPath = $service.Backend.GetConfigurationContainerPath(`
    "ScheduledTasks")
$containerAdsPath = New-Object "Softerra.Adaxes.Adsi.AdsPath"`
    $scheduledTasksPath
$taskAdsPath = $containerAdsPath.CreateChildPath("CN=My Task")
$task = $service.OpenObject($taskAdsPath.ToString(), $null, $null, 0)
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
using Softerra.Adaxes.Interop.Adsi.ScheduledTasks;
class Program
{
    static void Main(string[] args)
    {
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        string scheduledTasksPath = service.Backend.GetConfigurationContainerPath(
            "ScheduledTasks");
        AdsPath containerAdsPath = new AdsPath(scheduledTasksPath);
        AdsPath taskAdsPath = containerAdsPath.CreateChildPath("CN=My Task");
        IAdmScheduledTask task =
            (IAdmScheduledTask)service.OpenObject(taskAdsPath.ToString(), null, null, 0);
    }
}

For information on how to manage Adaxes configuration objects, see the section Managing Adaxes Configuration.

Reading object properties

Directory object data is stored in its properties. For example, the first name of a user is stored in the givenName property of the user object. Some object properties are required while others are optional.

To read properties of a directory object, you can use the IADs interface that is supported by all ADSI objects. To read a property, you need to call the Get or GetEx method of the IADs interface and pass the name of the property you want to read as the parameter of the method.

The following code sample gets the username of a user account. The username is stored in the userPrincipalName property.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        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");
    }
}

Apart from the IADs interface, you can also use other ADSI interfaces to read properties of directory objects. The interfaces you can use depend on the type of the directory object. For more information, see Interfaces supported by directory objects.

The following code sample gets the Terminal Services profile path of a user by reading the TerminalServicesProfilePath property of the IADsTSUserEx interface.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        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;
    }
}

Modifying object properties

To modify properties of a directory object you can use the Put and PutEx methods of the IADs interface. Both methods only make changes to property values contained in the property cache of the object. In order to commit changes to the directory, you need to call IADs::SetInfo.

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

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        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:

Creating objects

To create a new directory object, first you need to bind to the organizational unit or container where you want to create the object. All containers and organizational units expose the IADsContainer interface. To create a new object, you need to call the IADsContainer::Create method and pass in the object class and relative distinguished name (RDN) of the object being created. The Create method returns an instance of an ADSI object that represents the new directory object. Then you need to set object properties by calling IADs::Put and IADs::PutEx methods of the returned ADSI object. To save the object to the directory, call IADs::SetInfo.

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

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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 First 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)
    {
        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 First Logon
        ((IADsUser)user).AccountDisabled = false;

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

See also:

Deleting objects

To delete a directory object, you need to bind to the object and call the DeleteObject method of the IADsDeleteOps interface. The IADsDeleteOps interface is supported by all directory objects.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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);
    }
}

Copying objects

To copy a directory object, first you need to bind to the target organizational unit or container where you want the copied object to be placed. All containers and organizational units support the IADsContainer interface. To copy a directory object, you need to call the CopyHere method of the IADsContainer interface. The first parameter of the method is the ADS path of the object to be copied. The second parameter is the relative distinguished name (RDN) of the new directory object.

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

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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 First Logon

# 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)
    {
        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 First Logon

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

See also:

Moving objects

To move a directory object from one location in the directory tree to another, first you need to bind to the container or organizational unit where you want to move the object. All containers and organizational units support the IADsContainer interface. To move a directory object, you need to call the MoveHere method of the IADsContainer interface. The first parameter of the method is the ADS path of the object to be moved.

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

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        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);
    }
}

Renaming objects

To rename a directory object, you need to modify the name property of the object:

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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("name", "SMITH, John")
$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)
    {
        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("name", "SMITH, John");
        user.SetInfo();
    }
}

Enumerating child objects

To obtain a list of directory objects located in a container (or organizational unit), first you need to bind to the container. All containers and organizational units support the IEnumVARIANT interface that can be used to enumerate child objects. If you want to enumerate only child objects of specific types, you can use the IADsContainer::Filter property.

The following code sample enumerates all users and groups in an organizational unit.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

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

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

$container.Filter = @("user", "group")

foreach ($child in $container)
{
    Write-Host $child.ADsPath
}
C#
using System;
using Softerra.Adaxes.Interop.Adsi;
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADsContainer container = (IADsContainer)service.OpenObject(
            "Adaxes://OU=Sales,DC=company,DC=com", null, null, 0);

        container.Filter = new object[] {"user", "group"};

        foreach (IADs child in container)
        {
            Console.WriteLine(child.ADsPath);
        }
    }
}

See also:

Searching for objects

Using Adaxes ADSI Provider, you can search for directory objects and Adaxes-specific objects, such as security roles, business rules, and scheduled tasks. To perform a query in the directory, first you need to bind to the directory object under which you want to search. All directory objects support the IAdmDirectorySearcher interface. Using the IAdmDirectorySearcher::Criteria property, you can define the search criteria. The IAdmDirectorySearcher::SearchScope property specifies the search scope.

To start searching, you need to call the IAdmDirectorySearcher::ExecuteSearch method that returns the IAdmSearchResultIterator interface. To fetch all search results, call IAdmSearchResultIterator::FetchAll. Each search result is represented by the IAdmSearchResult interface.

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

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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:

Adding and removing group members

To add or remove members from a group, first you need to bind to the group. Each group object supports the IADsGroup interface. To add an object to a group, call the IADsGroup::Add method; to remove an object from a group, call IADsGroup::Remove. Both methods take the ADS path of the directory object to be added or removed from the group.

The following code sample adds John Smith and removes Ann Wilson from a group.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

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

$group = $service.OpenObject("Adaxes://CN=My Group,DC=company,DC=com", $null, $null, 0)

$group.Add("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com")
$group.Remove("Adaxes://CN=Ann Wilson,CN=Users,DC=company,DC=com")
C#
using Softerra.Adaxes.Adsi;
using Softerra.Adaxes.Interop.Adsi.PersistentObjects;
class Program
{
    static void Main(string[] args)
    {
        AdmNamespace ns = new AdmNamespace();
        IAdmService service = ns.GetServiceDirectly("localhost");

        IADsGroup group = (IADsGroup)service.OpenObject(
            "Adaxes://CN=My Group,DC=company,DC=com", null, null, 0);

        group.Add("Adaxes://CN=John Smith,CN=Users,DC=company,DC=com");
        group.Remove("Adaxes://CN=Ann Wilson,CN=Users,DC=company,DC=com");
    }
}

See also:

Getting operation Execution log

When performing operations on directory objects, Adaxes may execute additional actions. For example, you may have a business rule that automatically creates an Exchange mailbox and home folder for newly created users.

The information about additional actions is recorded in the Execution Log of an operation. To get the Execution Log of the last operation performed on a directory object, you can use the IAdmLastOperationOps interface. The IAdmLastOperationOps interface is supported by all directory objects. The GetLastOperationInfo method of the interface returns the IAdmOperationInfo interface. To get the operation Execution Log, you can use the IAdmOperationInfo::ExecutionLog property. The Execution Log is represented by the IAdmExecutionLogEntryCollection interface. Execution Log records are represented by the IAdmExecutionLogEntry interface.

The following code sample outputs the Execution Log of a user creation operation.

PowerShell
[Reflection.Assembly]::LoadWithPartialName("Softerra.Adaxes.Adsi")

$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)
    {
        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());
    }
}

See also