Connect to Active Directory With the PowerShell Connector

This sample shows an implementation of the PowerShell Connector toolkit and provides a number of PowerShell scripts that let you perform basic CRUD (create, read, update, delete) operations on an Active Directory server.

The sample uses the MS Active Directory PowerShell module. For more information on this module, see the corresponding Microsoft documentation.

Sample Overview

The generic PowerShell Connector Toolkit enables you to run PowerShell scripts on any external resource. The PowerShell Connector Toolkit is not a complete connector, in the traditional sense. Rather, it is a framework within which you must write your own PowerShell scripts to address the requirements of your Microsoft Windows ecosystem. You can use the PowerShell Connector Toolkit to create connectors that can provision any Microsoft system.

The PowerShell Connector Toolkit is available from the ForgeRock BackStage download site.

This sample assumes that IDM is running on a Windows system on the localhost. It also assumes that Active Directory and the ICF .NET connector server run on a remote Windows server. The PowerShell connector runs on the .NET connector server.

To use this sample for IDM instances installed on UNIX systems, adjust the relevant commands shown with PowerShell prompts.

Note

By default, the Get-ADUser and Get-ADGroup cmdlets are not thread safe. To avoid thread issues when you use this connector with Active Directory, you must set the pooling configuration properties as follows:

"UseInterpretersPool" : true,
"MinInterpretersPoolSize" : 1,
"MaxInterpretersPoolSize" : 10

For more information about these properties see "Configuring the PowerShell Connector".

Prepare the Sample

Run the commands in this procedure from the PowerShell command line. The continuation character used in the command is the back-tick (`).

  1. Install, configure, and start the .NET connector server on the machine that is running an Active Directory Domain Controller or on a workstation on which the Microsoft Active Directory PowerShell module is installed.

    For instructions on installing the .NET connector server, see "Set Up a .NET Connector Server".

  2. Configure IDM to connect to the .NET connector server.

  3. Download the PowerShell Connector Toolkit archive (mspowershell-connector-1.4.7.0.zip) from the ForgeRock BackStage download site.

    Extract the archive and move the MsPowerShell.Connector.dll to the folder in which the connector server application (connectorserver.exe) is located.

  4. Copy the PowerShell scripts and the ADSISearch module from the samples\scripted-powershell-with-ad\tools directory, to the machine on which the connector server is installed.

    dir \path\to\openidm\samples\scripted-powershell-with-ad\tools
    Directory: C:\path\to\openidm\samples\scripted-powershell-with-ad\tools
    
    Mode                LastWriteTime         Length Name
    ----                -------------         ------ ----
    -a----         4/3/2018   3:26 AM           4279 ADAuthenticate.ps1
    -a----         4/3/2018   3:26 AM           9055 ADCreate.ps1
    -a----         4/3/2018   3:26 AM           3717 ADDelete.ps1
    -a----         4/3/2018   3:26 AM          10756 ADSchema.ps1
    -a----         4/3/2018   3:26 AM           4625 ADSearch.ps1
    -a----         4/3/2018   3:26 AM           8064 ADSISearch.psm1
    -a----         4/3/2018   3:26 AM           5918 ADSync.ps1
    -a----         4/3/2018   3:26 AM           2408 ADTest.ps1
    -a----         4/3/2018   3:26 AM          18406 ADUpdate.ps1
    PS C:\path\to\openidm>
  5. Copy the sample connector configuration for the PowerShell connector to your project's conf directory.

    cp \path\to\openidm\samples\example-configurations\provisioners\provisioner.openicf-adpowershell.json \path\to\openidm\conf

    The following excerpt of the sample connector configuration shows the configuration properties:

    "configurationProperties" : {
        "AuthenticateScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADAuthenticate.ps1",
        "CreateScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADCreate.ps1",
        "DeleteScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADDelete.ps1",
        "SchemaScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADSchema.ps1",
        "SearchScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADSearch.ps1",
        "SyncScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADSync.ps1",
        "TestScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADTest.ps1",
        "UpdateScriptFileName" : "C:/openidm/samples/scripted-powershell-with-ad/tools/ADUpdate.ps1",
        "VariablesPrefix" : "Connector",
        "QueryFilterType" : "Ldap",
        "ReloadScriptOnExecution" : true,
        "UseInterpretersPool" : true,
        "SubstituteUidAndNameInQueryFilter" : true,
        "UidAttributeName" : "ObjectGUID",
        "NameAttributeName" : "DistinguishedName",
        "PsModulesToImport" : [
            "ActiveDirectory",
            "C:/openidm/samples/scripted-powershell-with-ad/tools/ADSISearch.psm1"
        ],
        "Host" : "",
        "Port" : null,
        "Login" : "",
        "Password" : null,
        "CustomProperties" : ["baseContext = CN=Users,DC=example,DC=com" ],
        "MinInterpretersPoolSize" : 1,
        "MaxInterpretersPoolSize" : 10
    },

    The sample connector configuration assumes that the scripts are located in C:/openidm/samples/scripted-powershell-with-ad/tools/. If you copied your scripts to a different location, or are using a different base context for search and synchronization operations such as DC=example,DC=org, adjust your connector configuration file accordingly.

    Note that the ICF framework requires the path to use forward slash characters and not the backslash characters that you would expect in a Windows path.

    The host, port, login and password of the machine on which Active Directory runs do not need to be specified here. By default the Active Directory cmdlets pick up the first available Domain Controller. In addition, the scripts are executed using the credentials of the .Net connector server.

    Note

    The ReloadScriptOnExecution property is set to true in this sample configuration. This setting causes script files to be reloaded each time the script is invoked. Having script files reloaded each time is suitable for debugging purposes. However, this property should be set to false in production environments, as the script reloading can have a negative performance impact.

    In addition, make sure that the value of the connectorHostRef property in the connector configuration file matches the value that you specified in the remote connector configuration file, in step 2 of this procedure. For example:

    "connectorHostRef" : "dotnet",

Run the Sample

Because you have copied all of the required configuration files into the default IDM project, you can start IDM with the default configuration (that is, without the -p option):

\path\to\openidm\startup.bat

When IDM has started, test the sample by using the curl command-line utility. The following examples test the scripts that were provided in the tools directory.

  1. Test the connector configuration, and whether IDM is able to connect to the .NET connector server with the following request:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --request POST `
    "http://localhost:8080/openidm/system?_action=test"
    [
      {
        "ok": true,
        "connectorRef": {
          "bundleVersion": "[1.4.2.0,1.5.0.0)",
          "bundleName": "MsPowerShell.Connector",
          "connectorName": "Org.ForgeRock.OpenICF.Connectors.MsPowerShell.MsPowerShellConnector"
        },
        "objectTypes": [
          "__ALL__",
          "group",
          "account"
        ],
        "config": "config/provisioner.openicf/adpowershell",
        "enabled": true,
        "name": "adpowershell"
      }
    ] 
  2. Query the users in your Active Directory with the following request:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --request GET `
    "http://localhost:8080/openidm/system/adpowershell/account?_queryId=query-all-ids"
    {
      "remainingPagedResults": -1,
      "pagedResultsCookie": null,
      "resultCount": 1257,
      "result": [
        {
          "_id": "7c41496a-9898-4074-a537-bed696b6be92"
        },
        {
          "_id": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9"
        },
        {
          "_id": "6feef4a0-b121-43dc-be68-a96703a49aba"
        },
    ...
  3. To return the complete record of a specific user, include the ID of the user in the URL. The following request returns the record for Steven Carter:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --request GET `
    "http://localhost:8080/openidm/system/adpowershell/account/6feef4a0-b121-43dc-be68-a96703a49aba"
    {
      "_id": "6feef4a0-b121-43dc-be68-a96703a49aba",
      "postalCode": null,
      "passwordNotRequired": false,
      "cn": "Steven Carter",
      "name": "Steven Carter",
      "trustedForDelegation": false,
      "uSNChanged": "47219",
      "manager": null,
      "objectGUID": "6feef4a0-b121-43dc-be68-a96703a49aba",
      "modifyTimeStamp": "11/27/2014 3:37:16 PM",
      "employeeNumber": null,
      "sn": "Carter",
      "userAccountControl": 512,
      "passwordNeverExpires": false,
      "displayName": "Steven Carter",
      "initials": null,
      "pwdLastSet": "130615726366949784",
      "scriptPath": null,
      "badPasswordTime": "0",
      "employeeID": null,
      "badPwdCount": "0",
      "accountExpirationDate": null,
      "userPrincipalName": "steve.carter@ad0.example.com",
      "sAMAccountName": "steve.carter",
      "mail": "steven.carter@example.com",
      "logonCount": "0",
      "cannotChangePassword": false,
      "division": null,
      "streetAddress": null,
      "allowReversiblePasswordEncryption": false,
      "description": null,
      "whenChanged": "11/27/2014 3:37:16 PM",
      "title": null,
      "lastLogon": "0",
      "company": null,
      "homeDirectory": null,
      "whenCreated": "6/23/2014 2:50:48 PM",
      "givenName": "Steven",
      "telephoneNumber": "555-2518",
      "homeDrive": null,
      "uSNCreated": "20912",
      "smartcardLogonRequired": false,
      "distinguishedName": "CN=Steven Carter,CN=Users,DC=example,DC=com",
      "createTimeStamp": "6/23/2014 2:50:48 PM",
      "department": null,
      "memberOf": [
        "CN=employees,DC=example,DC=com"
      ],
      "homePhone": null
    }
  4. Test that you can authenticate as one of the users in your Active Directory. The username that you specify here can be either an ObjectGUID, UPN, sAMAccountname or CN:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --header "Content-Type: application/json" `
    --request POST `
    --data "{
         \"username\" : \"Steven Carter\",
         \"password\" : \"Passw0rd\"
         }" `
    "http://localhost:8080/openidm/system/adpowershell/account?_action=authenticate"
    {
      "_id": "6feef4a0-b121-43dc-be68-a96703a49aba"
    }

    The request returns the ObjectGUID if the authentication is successful.

  5. You can return the complete record for a specific user, using the query filter syntax described in "Construct Queries".

    The following query returns the record for the guest user:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --request GET `
    "http://localhost:8080/openidm/system/adpowershell/account?_queryFilter=cn+eq+'guest'"
    {
      "remainingPagedResults": -1,
      "pagedResultsCookie": null,
      "resultCount": 1,
      "result": [
        {
          "_id": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9",
          "postalCode": null,
          "passwordNotRequired": true,
          "cn": "Guest",
          "name": "Guest",
          "trustedForDelegation": false,
          "uSNChanged": "8197",
          "manager": null,
          "objectGUID": "f2e08a5c-473f-4798-a2d5-d5cc27c862a9",
          "modifyTimeStamp": "6/9/2014 12:35:16 PM",
          "employeeNumber": null,
          "userAccountControl": 66082,
          "whenChanged": "6/9/2014 12:35:16 PM",
          "initials": null,
          "pwdLastSet": "0",
          "scriptPath": null,
          "badPasswordTime": "0",
          "employeeID": null,
          "badPwdCount": "0",
          "accountExpirationDate": null,
          "sAMAccountName": "Guest",
          "logonCount": "0",
          "cannotChangePassword": true,
          "division": null,
          "streetAddress": null,
          "allowReversiblePasswordEncryption": false,
          "description": "Built-in account for guest access to the computer/domain",
          "userPrincipalName": null,
          "title": null,
          "lastLogon": "0",
          "company": null,
          "homeDirectory": null,
          "whenCreated": "6/9/2014 12:35:16 PM",
          "givenName": null,
          "homeDrive": null,
          "uSNCreated": "8197",
          "smartcardLogonRequired": false,
          "distinguishedName": "CN=Guest,CN=Users,DC=example,DC=com",
          "createTimeStamp": "6/9/2014 12:35:16 PM",
          "department": null,
          "memberOf": [
            "CN=Guests,CN=Builtin,DC=example,DC=com"
          ],
          "homePhone": null,
          "displayName": null,
          "passwordNeverExpires": true
        }
      ]
    }
  6. Test that you can create a user on the Active Directory server by sending a POST request with the create action.

    The following request creates the user Jane Doe on the Active Directory server:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --header "Content-Type: application/json" `
    --request POST `
    --data '{
      \"distinguishedName\" : \"CN=Jane Doe,CN=Users,DC=EXAMPLE,DC=COM\",
      \"sn\" : \"Doe\",
      \"cn\" : \"Jane Doe\",
      \"sAMAccountName\" : \"sample\",
      \"userPrincipalName\" : \"janedoe@example.com\",
      \"enabled\" : true,
      \"password\" : \"Passw0rd\",
      \"telephoneNumber\" : \"0052-611-091\"
     }' `
    "http://localhost:8080/openidm/system/adpowershell/account?_action=create"
    {
      "_id": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
      "title": null,
      "uSNCreated": "47244",
      "pwdLastSet": "130615892934093041",
      "cannotChangePassword": false,
      "telephoneNumber": "0052-611-091",
      "smartcardLogonRequired": false,
      "badPwdCount": "0",
      "department": null,
      "distinguishedName": "CN=Jane Doe,CN=Users,DC=example,DC=com",
      "badPasswordTime": "0",
      "employeeID": null,
      "cn": "Jane Doe",
      "division": null,
      "description": null,
      "userPrincipalName": "janedoe@example.com",
      "passwordNeverExpires": false,
      "company": null,
      "memberOf": [],
      "givenName": null,
      "streetAddress": null,
      "sn": "Doe",
      "initials": null,
      "logonCount": "0",
      "homeDirectory": null,
      "employeeNumber": null,
      "objectGUID": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
      "manager": null,
      "lastLogon": "0",
      "trustedForDelegation": false,
      "scriptPath": null,
      "allowReversiblePasswordEncryption": false,
      "modifyTimeStamp": "11/27/2014 8:14:53 PM",
      "whenCreated": "11/27/2014 8:14:52 PM",
      "whenChanged": "11/27/2014 8:14:53 PM",
      "accountExpirationDate": null,
      "name": "Jane Doe",
      "displayName": null,
      "homeDrive": null,
      "passwordNotRequired": false,
      "createTimeStamp": "11/27/2014 8:14:52 PM",
      "uSNChanged": "47248",
      "sAMAccountName": "sample",
      "userAccountControl": 512,
      "homePhone": null,
      "postalCode": null
    }
  7. Test that you can update a user object on the Active Directory server by sending a PUT request with the complete object, including the user ID in the URL.

    The following request updates user Jane Doe's entry, including her ID in the request. The update sends the same information that was sent in the create request, but adds an employeeNumber:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --header "Content-Type: application/json" `
    --header "If-Match: *" `
    --request PUT `
    --data '{
      \"distinguishedName\" : \"CN=Jane Doe,CN=Users,DC=EXAMPLE,DC=COM\",
      \"sn\" : \"Doe\",
      \"cn\" : \"Jane Doe\",
      \"userPrincipalName\" : \"janedoe@example.com\",
      \"enabled\" : true,
      \"password\" : \"Passw0rd\",
      \"telephoneNumber\" : \"0052-611-091\",
      \"employeeNumber\": \"567893\"
     }' `
    "http://localhost:8080/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf"
    {
      "_id": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
      "title": null,
      "uSNCreated": "47244",
      "pwdLastSet": "130615906375709689",
      "cannotChangePassword": false,
      "telephoneNumber": "0052-611-091",
      "smartcardLogonRequired": false,
      "badPwdCount": "0",
      "department": null,
      "distinguishedName": "CN=Jane Doe,CN=Users,DC=example,DC=com",
      "badPasswordTime": "0",
      "employeeID": null,
      "cn": "Jane Doe",
      "division": null,
      "description": null,
      "userPrincipalName": "janedoe@example.com",
      "passwordNeverExpires": false,
      "company": null,
      "memberOf": [],
      "givenName": null,
      "streetAddress": null,
      "sn": "Doe",
      "initials": null,
      "logonCount": "0",
      "homeDirectory": null,
      "employeeNumber": "567893",
      "objectGUID": "42725210-8dce-4fdf-b0e0-393cf0377fdf",
      "manager": null,
      "lastLogon": "0",
      "trustedForDelegation": false,
      "scriptPath": null,
      "allowReversiblePasswordEncryption": false,
      "modifyTimeStamp": "11/27/2014 8:37:17 PM",
      "whenCreated": "11/27/2014 8:14:52 PM",
      "whenChanged": "11/27/2014 8:37:17 PM",
      "accountExpirationDate": null,
      "name": "Jane Doe",
      "displayName": null,
      "homeDrive": null,
      "passwordNotRequired": false,
      "createTimeStamp": "11/27/2014 8:14:52 PM",
      "uSNChanged": "47253",
      "sAMAccountName": "sample",
      "userAccountControl": 512,
      "homePhone": null,
      "postalCode": null
    }     
  8. Test whether you are able to delete a user object on the Active Directory server by sending a DELETE request with the user ID in the URL.

    The following request deletes user Jane Doe's entry:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --request DELETE `
    "http://localhost:8080/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf"

    The response includes the complete user object that was deleted.

    You can you attempt to query the user object to confirm that it has been deleted:

    curl `
    --header "X-OpenIDM-Username: openidm-admin" `
    --header "X-OpenIDM-Password: openidm-admin" `
    --header "Accept-API-Version: resource=1.0"  `
    --request GET `
    "http://localhost:8080/openidm/system/adpowershell/account/42725210-8dce-4fdf-b0e0-393cf0377fdf"
    {
      "message": "",
      "reason": "Not Found",
      "code": 404
    }
Read a different version of :