OpenIDM - lancement PowerShell en postCreate AD

Contexte

Dans un environnement OpenIDM 3.1, nous utilisons le connecteur ActiveDirectory 1.4.0.0, le mspowershell-connector-1.4.1.0 et le connecteur ICF .Net 1.4.1.0 (openicf-zip-1.4.1.0-dotnet.msi).
L'idée est de lancer un script powershell à la création d'un utilisateur dans l'Active Directory, afin de positionner certains paramètres et créer le Home Directory.
Pour cela, une fois le connecteur installé sur le serveur Windows et après avoir testé la création d'un compte AD, nous allons modifier / créer plusieurs fichiers sur le serveur OpenIDM :

  • conf/provisioner.openicf-AD.json : la définition de notre connecteur AD. On y trouve notamment l'adresse du serveur, le domaine, etc
  • conf/sync.json : le fichier de mapping.
  • script/access.js : fichier de paramétrage des accès aux URL
  • script/AD/postCreateAD.js : script javascript appelé après un CREATE sur la ressource AD
  • script/PS/createHomeDir.ps1 : script powershell qui va effectuer les actions sur le serveur distant

Note : dans ce cas, on note que tous les scripts sont situés sur le serveur OpenIDM. Aucun script n'est présent sur le serveur Windows.

Fichiers de paramétrage JSON

conf/provisioner.openicf-AD.json

Dans le fichier de définition de la ressource ActiveDirectory, on ajoute un paramètre systemActions qui va déclarer les scripts susceptibles d'être lancés :

    .../...
    "syncFailureHandler" : {
        "maxRetries" : 5,
        "postRetryAction" : "logged-ignore"
    },

    "systemActions" : [
        {
            "scriptId" : "CreateHomeDirectory",
            "actions" : [
                {
                    "systemType" : ".*ActiveDirectoryConnector",
                    "actionType" : "PowerShell",
                    "actionFile" : "script/PS/createHomeDir.ps1"
                }
            ]
        }
    ],
    
    "objectTypes" : {
        "Group" : {
    .../...

On définit ici le type de système (connecteur Active Directory), le type d'action / script, et le chemin vers le fichier script PowerShell.

conf/sync.json

C'est dans ce fichier de mapping que l'on définit quel fichier lancer lors du postAction (c'est le seul type d'action disponible sur les règles / policies).

            "name" : "User_AD",
            "policies" : [
                {
                    "action" : "CREATE",
                    "postAction" : {
                        "type" : "text/javascript",
                        "file" : "script/AD/postCreateAD.js"
                    },
                    "situation" : "ABSENT"
                },

On définit ici le chemin vers le script Javascript.
Note : afin d'éviter les messages d'erreur, il vaut mieux créer le script Javascript (même vide) AVANT de modifier le fichier sync.json, sous peine d'invalider le mapping.

Fichiers de script

En dehors des scripts PowerShell et Javascript utilisés, il faut également modifier le fichier access.js pour autoriser l'appel à un service REST avec une action de type script.
Par défaut, en effet, l'appel de script directement est interdit.
Si on lance une requête REST avec une action "script", on récupère une erreur 403 - Access denied :

curl --header X-OpenIDM-Username: openidm-admin --header X-OpenIDM-Password: openidm-admin \
--header Content-Type: application/json --request POST \
--data {
  "homeDir" : "P:/home/jdoe",
  "adUser" : "jdoe"
} http://localhost:8080/openidm/system/AD/?_action=script&scriptId=CreateHomeDirectory

{"code":403,"reason":"Forbidden","message":"Access denied"}

script/access.js

Il faut ajouter l'autorisation de lancer une action de type script sur le endpoint system/AD (la ressource utilisée dans notre cas) :

    
        {
        "pattern" : "system/AD",
        "roles" : "openidm-admin",
        "methods" : "action", 
        "actions" : "script" 
        }, 

Pour plus de sécurité, il est recommandé de définir un endpoint spécifique, qui permet de filter les accès.

script/AD/postCreate.js

Ce script javascript sera lancé après la création d'un compte Active Directory (cf. le paramétrage dans conf/sync.json).
S'agissant d'un script OpenIDM, on peut y faire appel aux fonctions OpenIDM, et également accéder aux objets source. Dans notre exemple, nous allons construire l'attribut homeDir en fonction du userName :

var params = {
  "_action" : "script",
  "scriptId" : "CreateHomeDirectory",
  "scriptExecuteMode" : "resource"
};

var data= {
  "_action" : "script",
  "scriptId" : "CreateHomeDirectory",
  "adUser" : source.userName,
  "homeDir" : "P:\\home\\" + source.userName 
};

// OpenIDM 3.x syntax
var actout = openidm.action("system/AD", params, data);

Attention à la syntaxe des paramètres ! Après plusieurs tentatives infructueuses, il s'avère que le paramètre scriptExecuteMode doit être déclaré et positionné à la valeur "resource" pour que le script PowerShell soit exécuté par la ressource.
On remarque aussi que les paramètres action et scriptId doivent être définis tant dans l'object params que dans l'objet data...

script/PS/createHomeDir.ps1

Ce script est en PowerShell. Le mieux est de se rapprocher dans ce cas de l'équipe d'administrateurs Windows, qui se fera un plaisir de modifier les scripts qu'ils lancent manuellement.

Pour cet exemple, on crée le HomeDirectory, et on écrit dans un fichier de log les données qui sont transmises par le connecteur.

Voir cet exemple : See http://suneworld.com/2012/06/creating-home-directory-for-user/

#param($homeDir, $adUser)


try {
  if ( $adUser -eq $null ) {
  $Error_Message = [string]"Required variable 'adUser' is missing!"
  Write-Error $Error_Message
  throw $Error_Message
  }
else
{

$userprincipalname = $adUser
$homedir = $homeDir
if ( !(Test-Path -Path "$homedir\$userprincipalname" -PathType Container) ) {
   ## Doesn't exist so create it.
   Write-Error "home directory doesn't exist. Creating home directory."

   ## Create the directory
   New-Item -path $homedir -Name $userprincipalname -ItemType Directory
   $userDir = "$homedir\$userprincipalname"

   ## Modify  Permissions on homedir
   $Rights= [System.Security.AccessControl.FileSystemRights]::Read -bor [System.Security.AccessControl.FileSystemRights]::Write -bor [System.Security.AccessControl.FileSystemRights]::Modify -bor [System.Security.AccessControl.FileSystemRights]::FullControl
   $Inherit=[System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
   $Propogation=[System.Security.AccessControl.PropagationFlags]::None
   $Access=[System.Security.AccessControl.AccessControlType]::Allow
   $AccessRule = new-object System.Security.AccessControl.FileSystemAccessRule("$userprincipalname",$Rights,$Inherit,$Propogation,$Access)
   $ACL = Get-Acl $userDir
   $ACL.AddAccessRule($AccessRule)
   $Account = new-object system.security.principal.ntaccount($userprincipalname)
   $ACL.setowner($Account)
   $ACL.SetAccessRule($AccessRule)
   Set-Acl $userDir $ACL

}

  $text = "Creating HomeDirectory for Username " + $adUser
  $File ="C:\Scripts\Logs\createHomeDirs.log"
  # Create file:
  $text | Out-File $File -Append
  Write-Output "Created Home Dir : " $userDir
  Exit 0
  }
}
catch {
  Write-Output $("ERROR: " + $error[0].ToString() + $error[0].InvocationInfo.PositionMessage)
  Exit 1
}

En retour, on reçoit le contenu de la ligne "Write-Output" :

curl --header X-OpenIDM-Username: openidm-admin --header X-OpenIDM-Password: openidm-password \
--header Content-Type: application/json --request POST \
--data {
  "homeDir" : "C:/HomeDir",
  "adUser" : "jdoe"
} http://localhost:8080/openidm/system/AD/?_action=script&scriptId=CreateHomeDirectory

{"actions":[{"result":{"0":"C:\\HomeDir\\jdoe","1":"Created Home Dir : ","2":"C:\\HomeDir\\jdoe"}}]}

Références

Tout ceci est partiellement présent dans la documentation Forgerock : https://backstage.forgerock.com/#!/docs/openidm/3.1.0/integrators-guide….

Les principaux problèmes que j'ai rencontrés étaient liés à l'appel de la fonction openidm.action et aux paramètres à passer.
Si on ne remet pas le paramètre "action" dans les données, on a des messages d'erreurs, tels que :

  Caused by: org.forgerock.script.exception.ScriptThrownException: Wrapped org.forgerock.json.fluent.JsonValueException: /_action: Expecting a value (/opt/openidm/projet/dev/script/AD/postCreateAD.js#37) in /opt/openidm/projet/dev/script/AD/postCreateAD.js at line number 37 at column number 0 org.forgerock.json.fluent.JsonValueException: /_action: Expecting a value

 

Conclusion

Ceci permet de lancer certaines actions via le connecteur .Net, et justifie donc d'utiliser ce connecteur, par rapport à l'utilisation d'un connecteur LDAP pour gérer l'Active Directory.

Mais finalement, dans le cadre de notre projet, ce qui fonctionnait bien sur la plate-forme de test - un seul serveur AD - n'a pas été reconduit sur la production, où l'on avait de multiples serveurs AD, répartis entre l'Asie, l'Australie et l'Europe. Du coup, les délais de réplication des données entre les AD provoquaient souvent des problèmes car on essayait de lancer une création alors que le compte n'existait pas encore sur le serveur en question.

Pour finir, nous avons remplacé le lancement de ce script par le positionnement d'un attribut, traité par la suite à intervalle régulier par un script powershell. Du coup, nous aurions pu utiliser le connecteur LDAP, sauf que le PowerShell est également utilisé pour la gestion des boîtes aux lettres Exchange.

Catégorie